Baixe o app para aproveitar ainda mais
Prévia do material em texto
ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 1 de 33 Faculdade Ciência e Tecnologia Programação para dispositivos móveis Curso de Engenharia Informática Semestre: II Ano:2021 Resumo: Fundamentos Android (documento em revisão) 1. Definição: O Android é um conjunto de software para dispositivos móveis que inclui um sistema operacional, middleware e os principais aplicativos. O Android SDK fornece as ferramentas e APIs necessárias para começar a desenvolver aplicativos na plataforma Android usando a linguagem de programação Java. Caracteristicas • Application FrameWork:que permite a reutilização e substituição de componentes • Máquina virtual Dalvik: otimizada para dispositivos móveis; • Navegador integrado: baseado no motor WebKit de código aberto; • Gráficos otimizados: com base na biblioteca de gráficos 2D personalizada; Gráficos 3D baseados na especificação OpenGL ES 1.0 (aceleração de hardware opcional); • SQLite para armazenamento de dados estruturados; • Suporte de mídia para formatos comuns de áudio, vídeo e imagem estática (MPEG4, H.264, MP3, AAC, AMR, JPG, PNG, GIF); • Telefonia GSM (dependente de hardware); • Bluetooth, EDGE, 3G e WiFi (dependente do hardware); • Câmera, GPS, bússola e acelerômetro (dependente do hardware); • Ambiente de desenvolvimento rico, incluindo um emulador de dispositivo, ferramentas para depuração, perfil de memória e desempenho e um plug-in para o IDE Eclipse; ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 2 de 33 Arquitetura do Sistema Operativo Android O diagrama a seguir mostra os principais componentes do sistema operacional Android. Cada seção é descrita com mais detalhes abaixo. Desenvolvimento Android O Android vem com um conjunto de aplicativos principais, incluindo um cliente de e-mail, programa SMS, calendário, mapas, navegador, contatos e outros. Todos os aplicativos são escritos usando a linguagem de programação Java. No Android os desenvolvedores têm total acesso às mesmas APIs de estrutura usadas pelos aplicativos principais. A arquitetura do aplicativo é projetada para simplificar a reutilização de componentes; qualquer aplicativo pode publicar seus recursos e qualquer outro aplicativo pode então fazer uso desses recursos (sujeito às restrições de segurança impostas pela estrutura). Este mesmo mecanismo permite que os componentes sejam substituídos pelo usuário. O Android permite também: • Um conjunto rico e extensível de Views que podem ser usadas para construir um aplicativo, incluindo lists, grids, textboxes, buttons e até mesmo um navegador da web embutido ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 3 de 33 • Content providers que permitem aplicativos aceder dados de outros aplicativos (como Contatos) ou compartilhem seus próprios dados • Um Resource Manager, que permite acesso a recursos não codificados, como localized strings, gráficos e arquivos de layout • Um Notification Manager que permite que todos os aplicativos exibam alertas personalizados na barra de status • Um Activity Manager que gerencia o ciclo de vida dos aplicativos e fornece um backstack de navegação comum. Bibliotecas O Android inclui um conjunto de bibliotecas C / C ++ usadas por vários componentes do sistema Android. Esses recursos são expostos aos desenvolvedores por meio da estrutura do aplicativo Android. Algumas das bibliotecas principais estão listadas abaixo: • System C library- uma implementação derivada de BSD da biblioteca do sistema C padrão (libc), ajustada para dispositivos baseados em Linux embarcados; • Media Libraries- baseadas no OpenCORE da PacketVideo; as bibliotecas suportam reprodução e gravação de muitos formatos populares de áudio e vídeo, bem como arquivos de imagem estática, incluindo MPEG4, H.264, MP3, AAC, AMR, JPG e PNG • Surface Manager - gerência o acesso ao subsistema de exibição e compõe perfeitamente camadas gráficas 2D e 3D de vários aplicativos • LibWebCore - um mecanismo moderno de navegador da web que alimenta o navegador Android e uma visualização da web incorporável • SGL - o motor gráfico 2D subjacente • Bibliotecas 3D - uma implementação baseada em APIs OpenGL ES 1.0; as bibliotecas usam aceleração 3D de hardware (quando disponível) ou o rasterizador de software 3D altamente otimizado incluído • FreeType - renderização de bitmap e fonte vetorial • SQLite - um motor de banco de dados relacional leve e poderoso disponível para todos os aplicativos. Execução Android O Android inclui um conjunto de bibliotecas principais que fornece a maioria das funcionalidades disponíveis nas bibliotecas principais da linguagem de programação Java. Cada aplicativo Android é executado em seu próprio processo, com sua própria instância da máquina virtual Dalvik. Dalvik foi escrito para que um dispositivo possa executar múltiplas VMs com eficiência. O Dalvik VM executa arquivos no formato Dalvik Executable (.dex), que é otimizado para ocupar o mínimo de memória. A VM é baseada em registro e executa classes compiladas por um compilador de linguagem Java que foi transformado no formato .dex pela ferramenta "dx" incluída. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 4 de 33 O Dalvik VM depende do kernel Linux para funcionalidades subjacentes, como threading e gerenciamento de memória de baixo nível. Linux Kernel O Android depende do Linux core versão 2.6 para os principais serviços do sistema, como segurança, gerenciamento de memória, gerenciamento de processos, pilha de rede e modelo de driver. O kernel também atua como uma camada de abstração entre o hardware e o resto da pilha de software. Fundamentos de aplicações Android Os aplicativos Android são escritos na linguagem de programação Java. O código Java compilado - junto com quaisquer arquivos de dados e recursos exigidos pelo aplicativo - é agrupado pela ferramenta aapt em um pacote Android, um arquivo morto marcado por um sufixo .apk. Este arquivo é o veículo de distribuição do aplicativo e instalação em dispositivos móveis; é o arquivo que os usuários baixam para seus dispositivos. Todo o código em um único arquivo .apk é considerado um aplicativo. Os aplicativos Android tem as seguintes características: • Por padrão, cada aplicativo é executado em seu próprio processo Linux. O Android inicia o processo quando qualquer código do aplicativo precisa ser executado e desliga o processo quando não é mais necessário e os recursos do sistema são exigidos por outros aplicativos. • Cada processo tem sua própria máquina virtual Java (VM), de modo que o código do aplicativo é executado isoladamente do código de todos os outros aplicativos. • Por padrão, cada aplicativo é atribuído um ID de usuário Linux exclusivo. As permissões são definidas para que os arquivos do aplicativo fiquem visíveis apenas para aquele usuário e para o próprio aplicativo - embora também haja maneiras de exportá-los para outros aplicativos. É possível fazer com que dois aplicativos compartilhem o mesmo ID de usuário e, nesse caso, eles poderão ver os arquivos um do outro. Para conservar os recursos do sistema, os aplicativos com a mesma ID também podem ser executados no mesmo processo do Linux, compartilhando a mesma VM. Componentes da aplicação Um recurso central do Android é que um aplicativo pode fazer uso de elementos de outros aplicativos (desde que esses aplicativos permitam). Por exemplo, se seu aplicativo precisa exibir uma lista de rolagem de imagens e outro aplicativo desenvolveu um scroller adequado e o disponibilizou para outros, você pode chamar esse scroller para fazer o trabalho, em vez de ProgResSource: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 5 de 33 desenvolver o seu próprio. Seu aplicativo não incorpora o código do outro aplicativo ou link para ele. Em vez disso, ele simplesmente inicia aquela parte do outro aplicativo quando surge a necessidade. Para que isso funcione, o sistema deve ser capaz de iniciar um processo de aplicativo quando qualquer parte dele for necessária e instanciar os objetos Java para essa parte. Portanto, ao contrário dos aplicativos na maioria dos outros sistemas, os aplicativos Android não têm um único ponto de entrada para tudo no aplicativo (nenhuma função main (), por exemplo). Em vez disso, eles têm componentes essenciais que o sistema pode instanciar e executar conforme necessário. Existem quatro tipos de componentes: Activities Uma atividade apresenta uma interface de usuário visual para um esforço focado que o usuário pode realizar. Por exemplo, uma atividade pode apresentar uma lista de itens de menu que os usuários podem escolher ou pode exibir fotografias junto com suas legendas. Um aplicativo de mensagens de texto pode ter uma atividade que mostra uma lista de contatos para enviar mensagens, uma segunda atividade para escrever a mensagem para o contato escolhido e outras atividades para revisar mensagens antigas ou alterar configurações. Embora trabalhem juntos para formar uma interface de usuário coesa, cada atividade é independente das outras. Cada um é implementado como uma subclasse da classe base Activity. Um aplicativo pode consistir em apenas uma atividade ou, como o aplicativo de mensagens de texto que acabamos de mencionar, pode conter várias. Quais são as atividades, e quantas são, depende, é claro, do aplicativo e de seu design. Normalmente, uma das atividades é marcada como a primeira que deve ser apresentada ao usuário quando o aplicativo é iniciado. A mudança de uma atividade para outra é realizada fazendo com que a atividade atual comece a próxima. Cada atividade recebe uma janela padrão para desenhar. Normalmente, a janela preenche a tela, mas pode ser menor que a tela e flutuar sobre as outras janelas. Uma atividade também pode usar janelas adicionais - por exemplo, uma caixa de diálogo pop-up que pede uma resposta do usuário no meio da atividade ou uma janela que apresenta aos usuários informações vitais quando eles selecionam um determinado item na tela. O conteúdo visual da janela é fornecido por uma hierarquia de visualizações - objetos derivados da classe View base. Cada visualização controla um espaço retangular específico dentro da janela. As visualizações pai contêm e organizam o layout de seus filhos. As visualizações de folha ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 6 de 33 (aquelas na parte inferior da hierarquia) desenham nos retângulos que controlam e respondem às ações do usuário direcionadas a esse espaço. Assim, as visualizações são onde ocorre a interação da atividade com o usuário. Por exemplo, uma visualização pode exibir uma pequena imagem e iniciar uma ação quando o usuário tocar nessa imagem. O Android tem várias visualizações prontas que você pode usar - incluindo botões, campos de texto, barras de rolagem, itens de menu, caixas de seleção e muito mais. Uma hierarquia de visualização é colocada dentro da janela de uma atividade pelo método Activity.setContentView (). O content view é o objeto Visualização na raiz da hierarquia. Services O service não tem uma interface de usuário visual, mas é executado em segundo plano por um período indefinido de tempo. Por exemplo, um serviço pode tocar música de fundo enquanto o usuário atende a outros assuntos, ou pode buscar dados na rede ou calcular algo e fornecer o resultado para atividades que dele necessitem. Cada serviço estende a classe base de serviço. Um bom exemplo é um reprodutor de mídia reproduzindo músicas de uma lista de reprodução. O aplicativo do player provavelmente teria uma ou mais atividades que permitem ao usuário escolher as músicas e começar a reproduzi-las. No entanto, a reprodução da música em si não seria controlada por uma atividade porque os usuários esperam que a música continue tocando mesmo depois de deixarem o player e começarem algo diferente. Para manter a música tocando, a atividade do media player pode iniciar um serviço para ser executado em segundo plano. O sistema manterá o serviço de reprodução de música em execução, mesmo depois que a atividade que o iniciou sai da tela. É possível conectar-se a (bind to) um serviço em andamento (e iniciar o serviço se ainda não estiver em execução). Enquanto estiver conectado, você pode se comunicar com o serviço por meio de uma interface que o serviço expõe. Para o serviço de música, essa interface pode permitir que os usuários pausem, retrocedam, parem e reiniciem a reprodução. Como as atividades e outros componentes, os serviços são executados no encadeamento principal do processo do aplicativo. Para que não bloqueiem outros componentes ou a interface do usuário, eles geralmente geram outro thread para tarefas demoradas (como reprodução de música). ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 7 de 33 Broadcast receivers O broadcast receiver é um componente que não faz nada além de receber e reagir anúncios de transmissão. Muitas transmissões se originam no código do sistema - por exemplo, anúncios de que o fuso horário mudou, que a bateria está fraca, que uma foto foi tirada ou que o usuário mudou a preferência de idioma. Os aplicativos também podem iniciar transmissões - por exemplo, para permitir que outros aplicativos saibam que alguns dados foram baixados para o dispositivo e estão disponíveis para uso. Um aplicativo pode ter qualquer número de broadcast receivers para responder a qualquer anúncio que considere importante. Todos os receptores estendem a classe base BroadcastReceiver. Os receptores de transmissão não exibem uma interface de usuário. No entanto, eles podem iniciar uma atividade em resposta às informações que recebem ou podem usar o NotificationManager para alertar o usuário. As notificações podem chamar a atenção do usuário de várias maneiras - piscando a luz de fundo, vibrando o dispositivo, reproduzindo um som e assim por diante. Eles normalmente colocam um ícone persistente na barra de status, que os usuários podem abrir para receber a mensagem. Content providers Um Content providers disponibiliza um conjunto específico de dados do aplicativo para outros aplicativos. Os dados podem ser armazenados no sistema de arquivos, em um banco de dados SQLite ou de qualquer outra maneira que faça sentido. O provedor de conteúdo estende a classe base ContentProvider para implementar um conjunto padrão de métodos que permitem que outros aplicativos recuperem e armazenem dados do tipo que ele controla. No entanto, os aplicativos não chamam esses métodos diretamente. Em vez disso, eles usam um objeto ContentResolver e chamam seus métodos. Um ContentResolver pode falar com qualquer provedor de conteúdo; ele coopera com o provedor para gerenciar qualquer comunicação entre processos que esteja envolvida. Sempre que houver uma solicitação que deva ser tratada por um determinado componente, o Android garante que o processo do aplicativo do componente esteja em execução, iniciando-o se necessário, e que uma instância apropriada do componente esteja disponível, criando a instância se necessário. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 8 de 33 Activação de compoentes: intents Content providers são ativados quando são direcionados por uma solicitação de um ContentResolver. Os outros três componentes - atividades,serviços e receptores de transmissão - são ativados por mensagens assíncronas chamadas intents. Um intent é um objeto Intent que contém o conteúdo da mensagem. Para atividades e serviços, ele nomeia a ação solicitada e especifica o URI dos dados sobre os quais agir, entre outras coisas. Por exemplo, pode transmitir uma solicitação de atividade para apresentar uma imagem ao usuário ou permitir que o usuário edite algum texto. Para broadcast receivers, o objeto Intent nomeia a ação que está sendo anunciada. Por exemplo, pode anunciar às partes interessadas que o botão da câmera foi pressionado. Existem métodos separados para ativar cada tipo de componente: • Uma atividade é iniciada (ou recebe algo novo para fazer) passando um objeto Intent para Context.startActivity () ou Activity.startActivityForResult (). A atividade de resposta pode examinar a intenção inicial que causou seu lançamento, chamando seu método getIntent (). O Android chama o método onNewIntent () da atividade para transmitir quaisquer intents subsequentes. Freqüentemente, uma atividade inicia a próxima. Se ele espera um resultado da atividade que está iniciando, ele chama startActivityForResult () em vez de startActivity (). Por exemplo, se ele iniciar uma atividade que permite ao usuário escolher uma foto, pode esperar o retorno da foto escolhida. O resultado é retornado em um objeto Intent que é passado para o método onActivityResult () da atividade de chamada. • Um serviço é iniciado (ou novas instruções são fornecidas para um serviço em andamento) passando um objeto Intent para Context.startService (). O Android chama o método onStart () do serviço e passa para ele o objeto Intent. Da mesma forma, uma intenção pode ser passada para Context.bindService() para estabelecer uma conexão contínua entre o componente de chamada e um serviço de destino. O serviço recebe o objeto Intent em uma chamada onBind (). (Se o serviço ainda não estiver em execução, bindService () pode opcionalmente iniciá-lo.) Por exemplo, uma atividade pode estabelecer uma conexão com o serviço de reprodução de música mencionado anteriormente para que possa fornecer ao usuário os meios (uma interface de usuário) para controlar a reprodução. A atividade chamaria bindService () para configurar essa conexão e, em seguida, chamaria métodos definidos pelo serviço para afetar a reprodução. Um aplicativo pode iniciar uma transmissão passando um objeto Intent para métodos como Context.sendBroadcast(),Context.sendOrderedBroadcast() e Context.sendStickyBroadcast() em qualquer uma de suas variações. O Android entrega a intenção a todos os receptores de transmissão interessados chamando seus métodos onReceive (). ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 9 de 33 Shutting down dos componentes Um provedor de conteúdo está ativo apenas enquanto responde a uma solicitação de um ContentResolver. E um receptor de transmissão fica ativo apenas enquanto responde a uma mensagem de transmissão. Portanto, não há necessidade de encerrar explicitamente esses componentes. As atividades, por outro lado, fornecem a interface do usuário. Eles estão em uma longa conversa com o usuário e podem permanecer ativos, mesmo quando inativos, enquanto a conversa continuar. Da mesma forma, os serviços também podem permanecer em execução por muito tempo. Portanto, o Android tem métodos para encerrar atividades e serviços de maneira ordenada como descritos a seguir: • Uma atividade pode ser encerrada chamando seu método finish(). Uma atividade pode encerrar outra atividade (uma que começou com startActivityForResult ()) chamando finishActivity (). • Um serviço pode ser interrompido chamando seu método stopSelf () ou chamando Context.stopService (). Os componentes também podem ser desligados pelo sistema quando não estiverem mais sendo usados ou quando o Android precisar recuperar memória para componentes mais ativos. Manifest file Antes que o Android possa iniciar um componente de aplicativo, ele deve aprender que o componente existe. Portanto, os aplicativos declaram seus componentes em um arquivo de manifesto agrupado no pacote Android, o arquivo .apk que também contém o código, os arquivos e os recursos do aplicativo. O manifesto é um arquivo XML estruturado e é sempre denominado AndroidManifest.xml para todos os aplicativos. Ele faz um conjunto de coisas além de declarar os componentes do aplicativo, como nomear quaisquer bibliotecas às quais o aplicativo precisa ser vinculado (além da biblioteca padrão do Android) e identificar quaisquer permissões que o aplicativo espera receber. Mas a principal tarefa do manifesto é informar o Android sobre os componentes do aplicativo. Por exemplo, uma atividade pode ser declarada da seguinte maneira: <?xml version="1.0" encoding="utf-8"?> <manifest . . . > <application . . . > <activity android:name="com.example.project.FreneticActivity" android:icon="@drawable/small_pic.png" android:label="@string/freneticLabel" . . . > </activity> ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 10 de 33 . . . </application> </manifest> O atributo name do elemento <activity> nomeia a subclasse Activity que implementa a atividade. Os atributos de ícone e rótulo apontam para arquivos de recursos contendo um ícone e rótulo que podem ser exibidos aos usuários para representar a atividade. Os outros componentes são declarados de maneira semelhante - elementos <service> para serviços, elementos <receiver> para receptores de transmissão e elementos <provider> para provedores de conteúdo. Atividades, serviços e provedores de conteúdo que não são declarados no manifesto não são visíveis para o sistema e, conseqüentemente, nunca são executados. No entanto, os receptores de transmissão podem ser declarados no manifesto ou podem ser criados dinamicamente no código (como objetos BroadcastReceiver) e registrados no sistema chamando Context.registerReceiver (). Intent filters Um objeto Intent pode nomear explicitamente um componente de destino. Em caso afirmativo, o Android encontra esse componente (com base nas declarações no arquivo de manifesto) e o ativa. Mas se um destino não for nomeado explicitamente, o Android deve localizar o melhor componente para responder à intenção. Ele faz isso comparando o objeto Intent aos filtros de intent de alvos em potencial. Os filtros de intents de um componente informam ao Android os tipos de intents que o componente é capaz de manipular. Como outras informações essenciais sobre o componente, eles são declarados no arquivo de manifesto. Esta é uma extensão do exemplo anterior que adiciona dois filtros de intent à atividade: <?xml version="1.0" encoding="utf-8"?> <manifest . . . > <application . . . > <activity android:name="com.example.project.FreneticActivity" android:icon="@drawable/small_pic.png" android:label="@string/freneticLabel" . . . > <intent-filter . . . > <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter . . . > <action android:name="com.example.project.BOUNCE" /> <data android:mimeType="image/jpeg" /> ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 11 de 33 <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </activity> . . . </application> </manifest> O primeiro filtrono exemplo - a combinação da ação "android.intent.action.MAIN" e a categoria "android.intent.category.LAUNCHER" - é comum. Ele marca a atividade como aquela que deve ser representada no inicializador de aplicativos, a tela que lista os aplicativos que os usuários podem iniciar no dispositivo. Em outras palavras, a atividade é o ponto de entrada do aplicativo, o inicial que os usuários veriam ao escolher o aplicativo no inicializador. The second filter declares an action that the activity can perform on a particular type of data. A component can have any number of intent filters, each one declaring a different set of capabilities. If it doesn't have any filters, it can be activated only by intents that explicitly name the component as the target. For a broadcast receiver that's created and registered in code, the intent filter is instantiated directly as an IntentFilter object. All other filters are set up in the manifest. Activities and Tasks Conforme observado anteriormente, uma atividade pode iniciar outra, incluindo uma definida em um aplicativo diferente. Suponha, por exemplo, que você queira permitir que os usuários exibam um mapa de ruas de algum local. Já existe uma atividade que pode fazer isso, então tudo que sua atividade precisa fazer é juntar um objeto Intent com as informações necessárias e passá-lo para startActivity (). O visualizador de mapa exibirá o mapa. Quando o usuário pressiona a tecla BACK, sua atividade reaparece na tela. Para o usuário, parecerá que o visualizador de mapa faz parte do mesmo aplicativo que sua atividade, embora esteja definido em outro aplicativo e seja executado no processo desse aplicativo. O Android mantém essa experiência do usuário, mantendo ambas as atividades na mesma tarefa. Simplificando, uma tarefa é o que o usuário experimenta como um "aplicativo". É um grupo de atividades relacionadas, organizadas em uma pilha. A atividade raiz na pilha é aquela que iniciou a tarefa - normalmente, é uma atividade que o usuário selecionou no inicializador do aplicativo. A atividade no topo da pilha está em execução no momento - aquela que é o foco das ações do usuário. Quando uma atividade inicia outra, a nova atividade é colocada na pilha; torna- ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 12 de 33 se a atividade de corrida. A atividade anterior permanece na pilha. Quando o usuário pressiona a tecla BACK, a atividade atual é retirada da pilha e a anterior é retomada como a atividade em execução. A pilha contém objetos, portanto, se uma tarefa tiver mais de uma instância da mesma subclasse de Activity aberta - vários visualizadores de mapa, por exemplo - a pilha tem uma entrada separada para cada instância. As atividades na pilha nunca são reorganizadas, apenas empurradas e removidas. Uma tarefa é uma pilha de atividades, não uma classe ou um elemento no arquivo de manifesto. Portanto, não há como definir valores para uma tarefa independentemente de suas atividades. Os valores da tarefa como um todo são definidos na atividade raiz. Por exemplo, a próxima seção falará sobre a "afinidade de uma tarefa"; esse valor é lido do conjunto de afinidades para a atividade raiz da tarefa. Todas as atividades em uma tarefa se movem juntas como uma unidade. A tarefa inteira (toda a pilha de atividades) pode ser colocada em primeiro plano ou enviada para segundo plano. Suponha, por exemplo, que a tarefa atual tenha quatro atividades em sua pilha - três na atividade atual. O usuário pressiona a tecla HOME, vai para o inicializador do aplicativo e seleciona um novo aplicativo (na verdade, uma nova tarefa). A tarefa atual vai para o segundo plano e a atividade raiz para a nova tarefa é exibida. Então, após um curto período, o usuário volta para a tela inicial e seleciona novamente o aplicativo anterior (a tarefa anterior). Essa tarefa, com todas as quatro atividades na pilha, é apresentada. Quando o usuário pressiona a tecla VOLTAR, a tela não exibe a atividade que o usuário acabou de deixar (a atividade raiz da tarefa anterior). Em vez disso, a atividade no topo da pilha é removida e a atividade anterior na mesma tarefa é exibida. O comportamento que acabamos de descrever é o comportamento padrão para atividades e tarefas. Mas existem maneiras de modificar quase todos os seus aspectos. A associação de atividades com tarefas e o comportamento de uma atividade dentro de uma tarefa são controlados pela interação entre sinalizadores definidos no objeto Intent que iniciou a atividade e atributos definidos no elemento <activity> da atividade no manifesto. Tanto o solicitante quanto o respondente têm uma palavra a dizer sobre o que acontece. A este respeito, os principais sinalizadores de Intenção são: ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 13 de 33 FLAG_ACTIVITY_NEW_TASK FLAG_ACTIVITY_CLEAR_TOP FLAG_ACTIVITY_RESET_TASK_IF_NEEDED FLAG_ACTIVITY_SINGLE_TOP Os principais atributos <activity> são: taskAffinity launchMode allowTaskReparenting clearTaskOnLaunch alwaysRetainTaskState finishOnTaskLaunch As seções a seguir descrevem o que alguns desses sinalizadores e atributos fazem, como eles interagem e quais considerações devem reger seu uso. Affinities and new tasks Por padrão, todas as atividades em um aplicativo têm afinidade entre si - ou seja, há uma preferência para que todas pertençam à mesma tarefa. No entanto, uma afinidade individual pode ser definida para cada atividade com o atributo taskAffinity do elemento <activity>. Atividades definidas em diferentes aplicativos podem compartilhar uma afinidade ou atividades definidas no mesmo aplicativo podem ser designadas a diferentes afinidades. A afinidade entra em ação em duas circunstâncias: quando o objeto Intent que inicia uma atividade contém o sinalizador FLAG_ACTIVITY_NEW_TASK e quando uma atividade tem seu atributo allowTaskReparenting definido como "true". A sinalização FLAG_ACTIVITY_NEW_TASK Conforme descrito anteriormente, uma nova atividade é, por padrão, iniciada na tarefa da atividade que chamou startActivity (). Ele é colocado na mesma pilha do chamador. No entanto, se o objeto Intent passado para startActivity () contiver o sinalizador FLAG_ACTIVITY_NEW_TASK, o sistema procurará uma tarefa diferente para hospedar a nova atividade. Freqüentemente, como o nome da bandeira indica, é uma nova tarefa. No entanto, não precisa ser assim. Se já houver uma tarefa existente com a mesma afinidade da nova atividade, a atividade será iniciada nessa tarefa. Caso contrário, ele inicia uma nova tarefa. O atributo allowTaskReparenting ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 14 de 33 Se uma atividade tiver seu atributo allowTaskReparenting definido como "true", ela pode passar da tarefa que inicia para a tarefa com a qual tem afinidade quando essa tarefa vier à tona. Por exemplo, suponha que uma atividade que relata as condições meteorológicas em cidades selecionadas seja definida como parte de um aplicativo de viagens. Ele tem a mesma afinidade que outras atividades no mesmo aplicativo (a afinidade padrão) e permite a correção novamente. Uma de suas atividades inicia o repórter meteorológico, portanto, inicialmente ela pertence à mesma tarefa que sua atividade. No entanto, quando o aplicativo de viagem for apresentado em seguida, o repórter do tempo será reatribuído e exibido com essa tarefa. Se um arquivo .apk contiver mais de um "aplicativo" do ponto de vista do usuário, você provavelmente desejará atribuir diferentes afinidades às atividades associadas a cada um deles. Launch modes Existem quatro modos de inicialização diferentes que podem ser atribuídos a um atributo launchMode do elemento <activity>:"standard" (the default mode) "singleTop" "singleTask" "singleInstance" Os modos diferem uns dos outros nestes quatro pontos: • Qual tarefa conterá a atividade que responde à intenção. Para os modos "padrão" e "singleTop", é a tarefa que originou o intent (e chamado startActivity ()) - a menos que o objeto Intent contenha o sinalizador FLAG_ACTIVITY_NEW_TASK. Nesse caso, uma tarefa diferente é escolhida conforme descrito na seção anterior, Afinidades e novas tarefas. Em contraste, os modos "singleTask" e "singleInstance" marcam atividades que estão sempre na raiz de uma tarefa. Eles definem uma tarefa; eles nunca são lançados em outra tarefa. • Se pode haver várias instâncias da atividade. Uma atividade "padrão" ou "singleTop" pode ser instanciada várias vezes. Eles podem pertencer a várias tarefas e uma determinada tarefa pode ter várias instâncias da mesma atividade. Em contraste, as atividades "singleTask" e "singleInstance" são limitadas a apenas uma instância. Uma vez que essas atividades estão na raiz de uma tarefa, essa limitação significa que nunca há mais do que uma única instância da tarefa no dispositivo ao mesmo tempo. • Se a instância pode ter outras atividades em sua tarefa. Uma atividade "singleInstance" permanece sozinha como a única atividade em sua tarefa. Se ele ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 15 de 33 iniciar outra atividade, essa atividade será iniciada em uma tarefa diferente, independentemente de seu modo de inicialização - como se FLAG_ACTIVITY_NEW_TASK estivesse no intent. Em todos os outros aspectos, o modo "singleInstance" é idêntico a "singleTask". Os outros três modos permitem que várias atividades pertençam à tarefa. Uma atividade "singleTask" sempre será a atividade raiz da tarefa, mas pode iniciar outras atividades que serão atribuídas à sua tarefa. Instâncias de atividades "padrão" e "singleTop" podem aparecer em qualquer lugar em uma pilha. • Se uma nova instância da classe será iniciada para lidar com um novo intent. Para o modo "padrão" padrão, uma nova instância é criada para responder a cada nova intenção. Cada instância lida com apenas um intent. Para o modo "singleTop", uma instância existente da classe é reutilizada para lidar com uma nova intenção se ela residir no topo da pilha de atividades da tarefa de destino. Se não residir na parte superior, não é reutilizado. Em vez disso, uma nova instância é criada para o novo intent e colocada na pilha. Por exemplo, suponha que a pilha de atividades de uma tarefa consiste na atividade raiz A com as atividades B, C e D no topo nessa ordem, então a pilha é A-B-C-D. Uma intenção chega para uma atividade do tipo D. Se D tiver o modo de inicialização "padrão" padrão, uma nova instância da classe é iniciada e a pilha se torna A-B-C-D-D. No entanto, se o modo de inicialização de D for "singleTop", espera-se que a instância existente trate o novo intent (já que está no topo da pilha) e a pilha permanece A-B-C-D. Se, por outro lado, a intenção de chegada for para uma atividade do tipo B, uma nova instância de B seria iniciada, não importando se o modo de B é "padrão" ou "singleTop" (uma vez que B não está no topo da pilha ), então a pilha resultante seria ABCDB. Conforme observado acima, nunca há mais de uma instância de uma atividade "singleTask" ou "singleInstance", portanto, espera-se que essa instância trate de todos os novos intents. Uma atividade "singleInstance" está sempre no topo da pilha (uma vez que é a única atividade na tarefa), portanto, está sempre em posição de lidar com a intenção. No entanto, uma atividade "singleTask" pode ou não ter outras atividades acima dela na pilha. Em caso afirmativo, ele não está em posição de lidar com a intent, e a intent é descartada. (Mesmo que a intenção seja descartada, sua chegada faria com que a tarefa fosse para o primeiro plano, onde permaneceria.) Quando uma atividade existente é solicitada a manipular um novo intent, o objeto Intent é passado para a atividade em uma chamada onNewIntent (). (O objeto de intenção que originalmente iniciou a atividade pode ser recuperado chamando getIntent ().) Observe que quando uma nova instância de uma Activity é criada para lidar com um novo intent, o usuário pode sempre pressionar a tecla BACK para retornar ao estado anterior (para a atividade anterior). Mas quando uma instância existente de uma Activity trata de uma nova intent, o usuário não pode pressionar a tecla BACK para retornar ao que a instância estava fazendo antes da chegada da nova intent. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 16 de 33 Clearing the stack on Android Se o usuário deixar uma tarefa por um longo tempo, o sistema limpa a tarefa de todas as atividades, exceto a atividade raiz. Quando o usuário retorna à tarefa novamente, é como o usuário a deixou, exceto que apenas a atividade inicial está presente. A ideia é que, depois de um tempo, os usuários provavelmente terão abandonado o que estavam fazendo antes e retornarão à tarefa para começar algo novo. Existem alguns atributos de atividade que podem ser usados para controlar esse comportamento e modificá-lo: O atributo alwaysRetainTaskState • Se este atributo for definido como "verdadeiro" na atividade raiz de uma tarefa, o comportamento padrão recém-descrito não ocorre. A tarefa retém todas as atividades em sua pilha, mesmo após um longo período. O atributo clearTaskOnLaunch • Se este atributo for definido como "verdadeiro" na atividade raiz de uma tarefa, a pilha é limpa para a atividade raiz sempre que o usuário deixa a tarefa e retorna a ela. Em outras palavras, é o oposto de alwaysRetainTaskState. O usuário sempre retorna à tarefa em seu estado inicial, mesmo após uma ausência momentânea. O atributo finishOnTaskLaunch • Esse atributo é como clearTaskOnLaunch, mas opera em uma única atividade, não em uma tarefa inteira. E pode fazer com que qualquer atividade desapareça, incluindo a atividade raiz. Quando definido como "verdadeiro", a atividade permanece parte da tarefa apenas para a sessão atual. Se o usuário sair e retornar à tarefa, ela não estará mais presente. Existe outra maneira de forçar a remoção de atividades da pilha. Se um objeto Intent inclui o sinalizador FLAG_ACTIVITY_CLEAR_TOP e a tarefa de destino já tem uma instância do tipo de atividade que deve lidar com o intent em sua pilha, todas as atividades acima dessa instância são eliminadas para que fique no topo da pilha e pode responder à intenção. Se o modo de inicialização da atividade designada for "padrão", ela também será removida da pilha e uma nova instância será iniciada para lidar com a intenção de entrada. Isso ocorre porque uma nova instância é sempre criada para uma nova intenção quando o modo de inicialização é "padrão". ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 17 de 33 FLAG_ACTIVITY_CLEAR_TOP é mais frequentemente usado em conjunto com FLAG_ACTIVITY_NEW_TASK. Quando usados juntos, esses sinalizadores são uma forma de localizar uma atividade existente em outra tarefa e colocá-la em uma posição onde possa responder à intenção. Starting tasks Uma atividade é configurada como o ponto de entrada para uma tarefa, fornecendo a ela um filtro de intenção com "android.intent.action.MAIN" como a ação especificada e "android.intent.category.LAUNCHER" como a categoria especificada. (Há um exemplo desse tipo de filtro na seção Filtros de intenção anterior.) Um filtro desse tipo faz com que um ícone e um rótulo para a atividade sejam exibidos no inicializador de aplicativos, dando aos usuários uma maneira de iniciar a tarefa e retornar a qualquer momento após ter sido lançado. Esta segundahabilidade é importante: os usuários devem ser capazes de sair de uma tarefa e voltar a ela mais tarde. Por esse motivo, os dois modos de inicialização que marcam as atividades como sempre iniciando uma tarefa, "singleTask" e "singleInstance", devem ser usados apenas quando a atividade tem um filtro PRINCIPAL e LAUNCHER. Imagine, por exemplo, o que poderia acontecer se o filtro estivesse faltando: um intent inicia uma atividade "singleTask", iniciando uma nova tarefa, e o usuário passa algum tempo trabalhando nessa tarefa. O usuário então pressiona a tecla HOME. A tarefa agora está ordenada atrás e obscurecida pela tela inicial. E, como não está representado no inicializador de aplicativos, o usuário não tem como retornar a ele. Uma dificuldade semelhante ocorre na bandeira FLAG_ACTIVITY_NEW_TASK. Se este sinalizador fizer com que uma atividade inicie uma nova tarefa e o usuário pressionar a tecla HOME para sair dela, deve haver alguma maneira de o usuário navegar de volta para ela novamente. Algumas entidades (como o gerenciador de notificação) sempre iniciam atividades em uma tarefa externa, nunca como parte delas, portanto, sempre colocam FLAG_ACTIVITY_NEW_TASK nos intents que passam para startActivity (). Se você tiver uma atividade que pode ser chamada por uma entidade externa que pode usar esse sinalizador, certifique-se de que o usuário tenha uma maneira independente de voltar à tarefa que foi iniciada. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 18 de 33 Processes and Threads Quando o primeiro componente de um aplicativo precisa ser executado, o Android inicia um processo Linux para ele com um único thread de execução. Por padrão, todos os componentes do aplicativo são executados nesse processo e thread. No entanto, você pode organizar os componentes para serem executados em outros processos e pode gerar threads adicionais para qualquer processo. Processes O processo em que um componente é executado é controlado pelo arquivo de manifesto. Os elementos do componente - <activity>, <service>, <receiver> e <provider> - cada um tem um atributo de processo que pode especificar um processo onde esse componente deve ser executado. Esses atributos podem ser definidos para que cada componente execute em seu próprio processo ou para que alguns componentes compartilhem um processo enquanto outros não. Eles também podem ser configurados para que componentes de diferentes aplicativos sejam executados no mesmo processo - desde que os aplicativos compartilhem o mesmo ID de usuário do Linux e sejam assinados pelas mesmas autoridades. O elemento <application> também possui um atributo de processo, para definir um valor padrão que se aplica a todos os componentes. Todos os componentes são instanciados no encadeamento principal do processo especificado e as chamadas do sistema para o componente são despachadas desse encadeamento. Threads separados não são criados para cada instância. Consequentemente, os métodos que respondem a essas chamadas - métodos como View.onKeyDown () que relatam as ações do usuário e as notificações de ciclo de vida discutidas posteriormente na seção Ciclos de vida do componente - sempre são executados no encadeamento principal do processo. Isso significa que nenhum componente deve realizar operações longas ou de bloqueio (como operações de rede ou loops de computação) quando chamado pelo sistema, pois isso bloqueará quaisquer outros componentes também no processo. Você pode gerar threads separados para operações longas, conforme discutido em Threads, a seguir. O Android pode decidir encerrar um processo em algum ponto, quando a memória estiver baixa e for exigida por outros processos que atendem mais imediatamente ao usuário. Conseqüentemente, ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 19 de 33 os componentes do aplicativo em execução no processo são destruídos. Um processo é reiniciado para esses componentes quando há trabalho novamente para eles fazerem. Ao decidir quais processos encerrar, o Android pondera sua importância relativa para o usuário. Por exemplo, ele desliga mais prontamente um processo com atividades que não são mais visíveis na tela do que um processo com atividades visíveis. A decisão de encerrar um processo, portanto, depende do estado dos componentes em execução nesse processo. Esses estados são o assunto de uma seção posterior, Ciclos de vida do componente. Threads Mesmo que você possa limitar seu aplicativo a um único processo, provavelmente haverá momentos em que você precisará gerar um thread para fazer algum trabalho em segundo plano. Como a interface do usuário deve sempre responder rapidamente às ações do usuário, o thread que hospeda uma atividade não deve hospedar operações demoradas, como downloads de rede. Qualquer coisa que não possa ser concluída rapidamente deve ser atribuída a um thread diferente. Threads são criados no código usando objetos de Thread Java padrão. O Android fornece várias classes de conveniência para gerenciar threads - Looper para executar um loop de mensagem em um thread, Handler para processar mensagens e HandlerThread para configurar um thread com um loop de mensagem. Remote procedure calls O Android tem um mecanismo leve para chamadas de procedimento remoto (RPCs) - onde um método é chamado localmente, mas executado remotamente (em outro processo), com qualquer resultado retornado ao chamador. Isso envolve decompor a chamada do método e todos os seus dados de atendimento em um nível que o sistema operacional possa entender, transmitindo-os do processo local e espaço de endereço para o processo remoto e espaço de endereço e remontando e reencenando a chamada lá. Os valores de retorno devem ser transmitidos na direção oposta. O Android fornece todo o código para fazer esse trabalho, para que você possa se concentrar na definição e implementação da própria interface RPC. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 20 de 33 Uma interface RPC pode incluir apenas métodos. Todos os métodos são executados de forma síncrona (o método local bloqueia até que o método remoto termine), mesmo se não houver valor de retorno. Em resumo, o mecanismo funciona da seguinte maneira: você começaria declarando a interface RPC que deseja implementar usando um IDL simples (linguagem de definição de interface). A partir dessa declaração, a ferramenta aidl gera uma definição de interface Java que deve ser disponibilizada para o processo local e remoto. Ele contém duas classes internas, conforme mostrado no diagrama a seguir: As classes internas têm todo o código necessário para administrar chamadas de procedimento remoto para uma interface que você cria com o IDL. Ambas as classes internas implementam uma interface IBinder. Um deles é usado localmente e internamente pelo sistema; o código que você pode ignorar. O outro, chamado Stub, estende a classe Binder. Além do código interno para efetuar as chamadas IPC, ele contém declarações para os métodos na interface RPC que você possui. Você criaria uma subclasse de Stub para implementar esses métodos, conforme indicado no diagrama. Normalmente, o processo remoto seriado gerenciado por um serviço (porque um pode informar o sistema sobre o processo e suas necessidades com outros processos). Ele teria o arquivo de ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 21 de 33 interface gerado pela ferramenta aidl e um subclasse Stub que implementa os métodos RPC. Os clientes do serviço acompanham apenas o arquivo de interface gerado pela ferramenta aidl. Veja como uma conexão entre um serviço e seus clientes éconfigurada: • Os clientes do serviço (no lado local) implementariam os métodos onServiceConnected () e onServiceDisconnected () para que possam ser notificados quando uma conexão bem-sucedida com o serviço remoto for estabelecida e quando ela for encerrada. Eles então chamariam bindService () para configurar a conexão. • O método onBind () do serviço seria implementado para aceitar ou rejeitar a conexão, dependendo da intenção que ele recebe (a intenção passada para bindService ()). Se a conexão for aceita, ele retorna uma instância da subclasse Stub. • Se o serviço aceitar a conexão, o Android chama o método onServiceConnected () do cliente e passa para ele um objeto IBinder, um proxy para a subclasse Stub gerenciada pelo serviço. Por meio do proxy, o cliente pode fazer chamadas no serviço remoto. Thread-safe methods Em alguns contextos, os métodos que você implementa podem ser chamados de mais de um encadeamento e, portanto, devem ser escritos para serem seguros para encadeamentos. Isso é verdadeiro principalmente para métodos que podem ser chamados remotamente - como no mecanismo RPC discutido na seção anterior. Quando uma chamada em um método implementado em um objeto, o IBinder se origina no mesmo processo que o IBinder, o método é executado no thread do chamador. No entanto, quando uma chamada se origina em outro processo, o método é obtido em um fio escolhido a partir de um pool de fios que o Android mantém nenhum processo que o IBinder; não é programado no thread principal do processo. Por exemplo, enquanto o método onBind () de um serviço seria chamado a partir do thread principal do processo do serviço, os métodos implementados nenhum objeto que onBind () retorna (por exemplo, uma subclasse Stub que implementa métodos RPC) tipos chamados de threads em a piscina. Como os serviços podem ter mais de um cliente, mais de um encadeamento de pool pode envolver o mesmo método IBinder ao mesmo tempo. Os métodos IB devem, portanto, ser implementados para serem thread-safe. Da mesma forma, um provedor de conteúdo pode receber solicitações de dados originadas em outros processos. Embora as classes ContentResolver e ContentProvider ocultem os detalhes de como a comunicação entre processos é gerenciada, os métodos ContentProvider que respondem a essas solicitações - os métodos query (), insert (), delete (), update () e getType () - são chamados de um pool de threads no processo do provedor de conteúdo, não o thread principal do processo. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 22 de 33 Uma vez que esses métodos podem ser chamados de qualquer número de threads ao mesmo tempo, eles também devem ser implementados para serem seguros para threads. Component Lifecycles Os componentes do aplicativo têm um ciclo de vida - um início quando o Android os instancia para responder às intenções até o fim quando as instâncias são destruídas. No meio, eles podem às vezes estar ativos ou inativos, ou, no caso de atividades, visíveis para o usuário ou invisíveis. Esta seção discute os ciclos de vida de atividades, serviços e receptores de transmissão - incluindo os estados em que eles podem estar durante suas vidas, os métodos que notificam você sobre as transições entre os estados e o efeito desses estados na possibilidade de o processo os hospedar pode ser encerrado e as instâncias destruídas. Activity lifecycle Uma atividade tem essencialmente três estados: • Ele está ativo ou em execução quando está no primeiro plano da tela (no topo da pilha de atividades da tarefa atual). Essa é a atividade que é o foco das ações do usuário. • Ele será pausado se tiver perdido o foco, mas ainda estiver visível para o usuário. Ou seja, outra atividade fica em cima dela e essa atividade é transparente ou não cobre a tela inteira, de modo que parte da atividade pausada pode ser exibida. Uma atividade pausada está completamente ativa (ela mantém todas as informações de estado e membro e permanece anexada ao gerenciador de janelas), mas pode ser eliminada pelo sistema em situações de memória extremamente baixa. • Ele é interrompido se estiver completamente obscurecido por outra atividade. Ainda retém todas as informações do estado e dos membros. No entanto, ele não está mais visível para o usuário, portanto, sua janela fica oculta e, com frequência, será eliminado pelo sistema quando houver necessidade de memória em outro lugar. Se uma atividade for pausada ou interrompida, o sistema pode eliminá-la da memória pedindo que ela termine (chamando seu método finish ()) ou simplesmente eliminando seu processo. Quando ele é exibido novamente para o usuário, ele deve ser completamente reiniciado e restaurado ao seu estado anterior. Conforme uma atividade faz a transição de um estado para outro, ela é notificada da mudança por chamadas para os seguintes métodos protegidos: void onCreate(Bundle savedInstanceState) void onStart() void onRestart() ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 23 de 33 void onResume() void onPause() void onStop() void onDestroy() Todos esses métodos são ganchos que você pode substituir para fazer o trabalho apropriado quando o estado muda. Todas as atividades devem implementar onCreate () para fazer a configuração inicial quando o objeto é instanciado pela primeira vez. Muitos também implementarão onPause () para confirmar alterações de dados e, de outra forma, se preparar para interromper a interação com o usuário. Calling into the superclass Uma implementação de qualquer método de ciclo de vida de atividade deve sempre primeiro chamar a versão da superclasse. Por exemplo: protected void onPause() { super.onPause(); . . . } Juntos, esses sete métodos definem todo o ciclo de vida de uma atividade. Existem três loops aninhados que você pode monitorar implementando-os: • Todo o tempo de vida de uma atividade acontece entre a primeira chamada para onCreate () até uma única chamada final para onDestroy (). Uma atividade faz toda a configuração inicial do estado "global" em onCreate () e libera todos os recursos restantes em onDestroy (). Por exemplo, se houver um encadeamento em execução em segundo plano para baixar dados da rede, ele pode criar esse encadeamento em onCreate () e, em seguida, interromper o encadeamento em onDestroy (). • O tempo de vida visível de uma atividade acontece entre uma chamada para onStart () até uma chamada correspondente para onStop (). Durante esse tempo, o usuário pode ver a atividade na tela, embora ela possa não estar em primeiro plano e interagindo com o usuário. Entre esses dois métodos, você pode manter os recursos necessários para mostrar a atividade ao usuário. Por exemplo, você pode registrar um BroadcastReceiver em onStart () para monitorar as alterações que afetam sua IU e cancelar o registro em onStop () quando o usuário não puder mais ver o que você está exibindo. Os métodos onStart () e onStop () podem ser chamados várias vezes, pois a atividade alterna entre ser visível e oculta para o usuário. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 24 de 33 • O tempo de vida em primeiro plano de uma atividade acontece entre uma chamada para onResume () até uma chamada correspondente para onPause (). Durante esse tempo, a atividade está na frente de todas as outras atividades na tela e está interagindo com o usuário. Uma atividade pode frequentemente fazer a transição entre os estados retomado e pausado - por exemplo, onPause () é chamado quando o dispositivo entra no modo de hibernação ou quando uma nova atividade é iniciada, onResume () é chamado quando um resultado de atividade ou uma nova intenção é entregue. Portanto, o códigonesses dois métodos deve ser bastante leve. O diagrama a seguir ilustra esses loops e os caminhos que uma atividade pode seguir entre os estados. Os ovais coloridos são os principais estados em que a atividade pode estar. Os retângulos quadrados representam os métodos de retorno de chamada que você pode implementar para realizar operações quando a atividade faz a transição entre os estados. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 25 de 33 ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 26 de 33 Observe a coluna Killable na tabela acima. Indica se o sistema pode interromper ou não o processo que hospeda a atividade a qualquer momento após o retorno do método, sem executar outra linha do código da atividade. Três métodos (onPause (), onStop () e onDestroy ()) são marcados como "Sim". Como onPause () é o primeiro dos três, é o único que tem garantia de ser chamado antes que o processo seja encerrado - onStop () e onDestroy () podem não ser. Portanto, você deve usar onPause () para gravar quaisquer dados persistentes (como edições do usuário) no armazenamento. Os métodos marcados com "Não" na coluna Eliminável protegem o processo que hospeda a atividade de ser eliminado a partir do momento em que são chamados. Assim, uma atividade está em um estado eliminável, por exemplo, do momento em que onPause () retorna até o momento em que onResume () é chamado. Não será possível eliminar novamente até que onPause () retorne novamente. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 27 de 33 Saving activity state Quando o sistema, em vez do usuário, desliga uma atividade para conservar a memória, o usuário pode esperar retornar à atividade e encontrá-la em seu estado anterior. Para capturar esse estado antes que a atividade seja eliminada, você pode implementar um método onSaveInstanceState () para a atividade. O Android chama esse método antes de tornar a atividade vulnerável a ser destruída - ou seja, antes de onPause () ser chamado. Ele passa ao método um objeto Bundle onde você pode registrar o estado dinâmico da atividade como pares nome-valor. Quando a atividade é iniciada novamente, o Bundle é passado para onCreate () e para um método que é chamado depois de onStart (), onRestoreInstanceState (), para que um ou ambos possam recriar o estado capturado. Ao contrário de onPause () e outros métodos discutidos anteriormente, onSaveInstanceState () e onRestoreInstanceState () não são métodos de ciclo de vida. Eles nem sempre são chamados. Por exemplo, o Android chama onSaveInstanceState () antes que a atividade se torne vulnerável a ser destruída pelo sistema, mas não se preocupa em chamá-la quando a instância está realmente sendo destruída por uma ação do usuário (como pressionar a tecla BACK). Nesse caso, o usuário não espera retornar à atividade, portanto, não há razão para salvar seu estado. Como onSaveInstanceState () nem sempre é chamado, você deve usá-lo apenas para registrar o estado transitório da atividade, não para armazenar dados persistentes. Use onPause () para esse propósito. Coordinating activities Quando uma atividade inicia outra, ambos passam por transições de ciclo de vida. Um faz uma pausa e pode parar, enquanto o outro inicia. Ocasionalmente, você pode precisar coordenar essas atividades, uma com a outra. A ordem dos retornos de chamada do ciclo de vida é bem definida, especialmente quando as duas atividades estão no mesmo processo: 1. O método onPause () da atividade atual é chamado. 2. Em seguida, os métodos onCreate (), onStart () e onResume () da atividade inicial são chamados em sequência. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 28 de 33 3. Então, se a atividade inicial não estiver mais visível na tela, seu método onStop () é chamado. Service lifecycle Um serviço pode ser usado de duas maneiras: • Ele pode ser iniciado e executado até que alguém o interrompa ou ele mesmo pare. Nesse modo, ele é iniciado chamando Context.startService() e interrompido chamando Context.stopService (). Ele pode se interromper chamando Service.stopSelf() ou Service.stopSelfResult(). Apenas uma chamada stopService() é necessária para interromper o serviço, não importa quantas vezes startService() foi chamado. • Ele pode ser operado programaticamente usando uma interface que define e exporta. Os clientes estabelecem uma conexão com o objeto Serviço e usam essa conexão para chamar o serviço. A conexão é estabelecida chamando Context.bindService() e é fechada chamando Context.unbindService(). Vários clientes podem se vincular ao mesmo serviço. Se o serviço ainda não foi iniciado, bindService () pode opcionalmente iniciá-lo. Os dois modos não são totalmente separados. Você pode vincular a um serviço que foi iniciado com startService (). Por exemplo, um serviço de música de fundo pode ser iniciado chamando startService () com um objeto Intent que identifica a música a tocar. Somente mais tarde, possivelmente quando o usuário deseja exercer algum controle sobre o reprodutor ou obter informações sobre a música atual, uma atividade estabeleceria uma conexão com o serviço chamando bindService (). Em casos como esse, stopService () não irá realmente parar o serviço até que a última ligação seja fechada. Como uma atividade, um serviço tem métodos de ciclo de vida que você pode implementar para monitorar mudanças em seu estado. Mas eles são menos do que os métodos de atividade - apenas três - e são públicos, não protegidos: void onCreate() void onStart(Intent intent) void onDestroy() Ao implementar esses métodos, você pode monitorar dois loops aninhados do ciclo de vida do serviço: • Todo o tempo de vida de um serviço acontece entre o momento em que onCreate () é chamado e o momento em que onDestroy () retorna. Como uma atividade, um serviço faz sua configuração inicial em onCreate () e libera todos os recursos restantes em onDestroy (). Por exemplo, um serviço de reprodução de música pode ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 29 de 33 criar o encadeamento onde a música será tocada em onCreate () e, em seguida, interromper o encadeamento em onDestroy (). • O tempo de vida ativo de um serviço começa com uma chamada para onStart (). Este método é entregue ao objeto Intent que foi passado para startService (). O serviço de música abriria o Intent para descobrir qual música tocar e iniciaria a reprodução. Não há retorno de chamada equivalente para quando o serviço é interrompido - nenhum método onStop (). Os métodos onCreate () e onDestroy () são chamados para todos os serviços, sejam eles iniciados por Context.startService () ou Context.bindService (). No entanto, onStart () é chamado apenas para serviços iniciados por startService (). Se um serviço permite que outros se vinculem a ele, existem métodos de retorno de chamada adicionais para que ele implemente: IBinder onBind(Intent intent) boolean onUnbind(Intent intent) void onRebind(Intent intent) O retorno de chamada onBind () é passado para o objeto Intent que foi passado para bindService e onUnbind () recebe o intent que foi passado para unbindService (). Se o serviço permitir a ligação, onBind () retorna o canal de comunicação que os clientes usam para interagir com o serviço. O método onUnbind () pode solicitar que onRebind () seja chamado se um novo cliente se conectar ao serviço. O diagrama a seguir ilustra os métodos de retorno de chamada para um serviço. Embora separe os serviços criados por meio de startService daqueles criados por bindService(), lembre-se de que qualquer serviço, independentemente de como seja iniciado, pode permitir que os clientes se vinculem a ele, portanto, qualquer serviço pode receber onBind () e onUnbind () chamadas. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 30 de 33 Broadcast receiver lifecycle Broadcast receiver lifecycle Um broadcast receiver tem um único método de retorno de chamada: void onReceive (Context curContext, Intent broadcastMsg) Quando uma mensagem de transmissão chega para o receptor, o Android chama seu método onReceive () e passa para o objeto Intent que contém a mensagem. O receptor de transmissão é considerado ativo apenas enquanto executa este método. Quando onReceive () retorna, ele está inativo. Um processo com um receptor de transmissão ativo está protegido contra eliminação. Mas um processo com apenas componentes inativos pode ser eliminado pelo sistema a qualquer momento, quando a memória que ele consome é necessária para outros processos. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 31 de 33 Isso apresenta um problema quando a resposta a uma mensagem de transmissão é demorada e, portanto, algo que deve ser feito em uma thread separada, longe da thread principal onde outros componentes da interface do usuário são executados. Se onReceive () gerar o encadeamento e retornar, todo o processo, incluindo o novo encadeamento, é considerado inativo (a menos que outros componentes do aplicativo estejam ativos no processo), colocando-o em risco de ser eliminado. A solução para esse problema é onReceive () iniciar um serviço e deixar o serviço fazer o trabalho, para que o sistema saiba que ainda há trabalho ativo sendo executado no processo. Processes and lifecycles O sistema Android tenta manter um processo de aplicativo pelo maior tempo possível, mas, eventualmente, precisará remover processos antigos quando a memória ficar baixa. Para determinar quais processos manter e quais eliminar, o Android coloca cada processo em uma "hierarquia de importância" com base nos componentes em execução e no estado desses componentes. Os processos com a menor importância são eliminados primeiro, depois aqueles com a próxima menor e assim por diante. Existem cinco níveis na hierarquia. A lista a seguir os apresenta em ordem de importância: 1. Um processo de primeiro plano é aquele que é necessário para o que o usuário está fazendo no momento. Um processo é considerado em primeiro plano se qualquer uma das seguintes condições for mantida: o Ele está executando uma atividade com a qual o usuário está interagindo (o método onResume () do objeto Activity foi chamado). o Ele hospeda um serviço vinculado à atividade com a qual o usuário está interagindo. o Ele tem um objeto Service que está executando um de seus retornos de chamada de ciclo de vida (onCreate (), onStart () ou onDestroy ()). o Ele tem um objeto BroadcastReceiver que está executando seu método onReceive (). Apenas alguns processos de primeiro plano existirão em um determinado momento. Eles são mortos apenas como último recurso - se a memória estiver tão baixa que eles não possam continuar em execução. Geralmente, nesse ponto, o dispositivo atingiu um estado de paginação de memória, portanto, eliminar alguns processos em primeiro plano é necessário para manter a interface do usuário responsiva. 2. Um processo visível é aquele que não tem nenhum componente de primeiro plano, mas ainda pode afetar o que o usuário vê na tela. Um processo é considerado visível se uma das seguintes condições for válida: o Ele hospeda uma atividade que não está em primeiro plano, mas ainda é visível para o usuário (seu método onPause () foi chamado). Isso pode ocorrer, por exemplo, se a atividade de primeiro plano for uma caixa de diálogo que permite que a atividade anterior seja vista atrás dela. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 32 de 33 o Ele hospeda um serviço vinculado a uma atividade visível. Um processo visível é considerado extremamente importante e não será eliminado, a menos que isso seja necessário para manter todos os processos em primeiro plano em execução. 3. Um processo de serviço é aquele que está executando um serviço que foi iniciado com o método startService () e que não se enquadra em nenhuma das duas categorias superiores. Embora os processos de serviço não estejam diretamente vinculados a nada que o usuário veja, eles geralmente fazem coisas com as quais o usuário se preocupa (como tocar um mp3 em segundo plano ou baixar dados na rede), de modo que o sistema os mantém em execução, a menos que não haja o suficiente memória para retê-los junto com todos os processos visíveis e em primeiro plano. 4. Um processo em segundo plano é aquele que contém uma atividade que não está atualmente visível para o usuário (o método onStop () do objeto Activity foi chamado). Esses processos não têm impacto direto na experiência do usuário e podem ser eliminados a qualquer momento para recuperar memória para um processo de primeiro plano, visível ou de serviço. Normalmente, há muitos processos em segundo plano em execução, portanto, eles são mantidos em uma lista de LRU (usados menos recentemente) para garantir que o processo com a atividade mais recentemente vista pelo usuário seja o último a ser eliminado. Se uma atividade implementa seus métodos de ciclo de vida corretamente e captura seu estado atual, eliminar seu processo não terá um efeito deletério na experiência do usuário. 5. Um processo vazio é aquele que não contém nenhum componente ativo do aplicativo. A única razão para manter esse processo é como um cache para melhorar o tempo de inicialização na próxima vez que um componente precisar ser executado nele. O sistema geralmente elimina esses processos para equilibrar os recursos gerais do sistema entre os caches de processo e os caches do kernel subjacentes. O Android classifica um processo no nível mais alto possível, com base na importância dos componentes atualmente ativos no processo. Por exemplo, se um processo hospeda um serviço e uma atividade visível, o processo será classificado como um processo visível, não um processo de serviço. Além disso, a classificação de um processo pode ser aumentada porque outros processos dependem dele. Um processo que está servindo a outro processo nunca pode ser classificado abaixo do processo que está servindo. Por exemplo, se um provedor de conteúdo no processo A estiver atendendo a um cliente no processo B, ou se um serviço no processo A estiver vinculado a um componente no processo B, o processo A sempre será considerado pelo menos tão importante quanto o processo B. ProgRes Source: https://www.linuxtopia.org/online_books/android/devguide/guide/topics/fundamentals.html Página 33 de 33 Como um processo que executa um serviço é classificado mais alto do que um com atividades em segundo plano, uma atividade que inicia uma operação de longa duração pode fazer bem em iniciar um serviço para essa operação, em vez de simplesmente gerar um thread - especialmente se a operação provavelmente durará mais do que o atividade. Exemplos disso são tocar música de fundo e enviar uma foto tirada pela câmera para um site. Usar um serviço garante que a operação terá pelo menos prioridade de "processo de serviço", independentemente do que aconteça com a atividade. Conforme observado na seção Ciclo de vida do receptor de transmissão anterior, esse é o mesmo motivo pelo qual os receptores de transmissão devem empregar serviços em vez de simplesmente colocar operações demoradas em um thread.
Compartilhar