Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
As atividades são um alicerce fundamental das aplicações Android e podem existir em vários estados diferentes. O ciclo de vida da atividade começa com instanciação e termina com destruição, e inclui muitos estados no meio. Quando uma atividade muda de estado, o método de evento de ciclo de vida apropriado é chamado, notificando a atividade da alteração de estado iminente e permitindo que ela execute código para se adaptar a essa alteração. Este artigo examina o ciclo de vida das atividades e explica a responsabilidade que uma atividade tem durante cada uma dessas mudanças de estado para fazer parte de um aplicativo bem comportado e confiável.
Visão geral do ciclo de vida da atividade
As atividades são um conceito de programação incomum específico para Android. No desenvolvimento de aplicativos tradicionais geralmente há um método principal estático, que é executado para iniciar o aplicativo. Com o Android, no entanto, as coisas são diferentes; As aplicações Android podem ser iniciadas através de qualquer atividade registada dentro de uma aplicação. Na prática, a maioria dos aplicativos terá apenas uma atividade específica que é especificada como o ponto de entrada do aplicativo. No entanto, se um aplicativo falhar ou for encerrado pelo sistema operacional, o sistema operacional pode tentar reiniciar o aplicativo na última atividade aberta ou em qualquer outro lugar dentro da pilha de atividades anterior. Além disso, o sistema operacional pode pausar as atividades quando elas não estão ativas e recuperá-las se estiver com pouca memória. Uma consideração cuidadosa deve ser feita para permitir que o aplicativo restaure corretamente seu estado no caso de uma atividade ser reiniciada, especialmente se essa atividade depender de dados de atividades anteriores.
O ciclo de vida da atividade é implementado como uma coleção de métodos que o SO chama ao longo do ciclo de vida de uma atividade. Esses métodos permitem que os desenvolvedores implementem a funcionalidade necessária para satisfazer os requisitos de gerenciamento de estado e recursos de seus aplicativos.
É extremamente importante para o desenvolvedor de aplicativos analisar os requisitos de cada atividade para determinar quais métodos expostos pelo ciclo de vida da atividade precisam ser implementados. A falha em fazer isso pode resultar em instabilidade de aplicativos, falhas, inchaço de recursos e, possivelmente, até instabilidade subjacente do sistema operacional.
Este capítulo examina o ciclo de vida da atividade em detalhes, incluindo:
- Estados de atividade
- Métodos de ciclo de vida
- Manter o estado de uma aplicação
Esta seção também inclui um passo a passo que fornece exemplos práticos sobre como salvar o estado de forma eficiente durante o ciclo de vida da Atividade. No final deste capítulo, você deve ter uma compreensão do ciclo de vida da atividade e como suportá-lo em um aplicativo Android.
Ciclo de vida da atividade
O ciclo de vida da atividade do Android compreende uma coleção de métodos expostos dentro da classe Activity que fornecem ao desenvolvedor uma estrutura de gerenciamento de recursos. Essa estrutura permite que os desenvolvedores atendam aos requisitos exclusivos de gerenciamento de estado de cada atividade dentro de um aplicativo e lidem adequadamente com o gerenciamento de recursos.
Estados de atividade
O sistema Android arbitra Atividades com base no seu estado. Isso ajuda o Android a identificar atividades que não estão mais em uso, permitindo que o sistema operacional recupere memória e recursos. O diagrama a seguir ilustra os estados pelos quais uma atividade pode passar durante sua vida:
Estes estados podem ser divididos em 4 grupos principais da seguinte forma:
Ativo ou em Execução – As atividades são consideradas ativas ou em execução se estiverem em primeiro plano, também conhecido como o topo da pilha de atividades. Esta é considerada a atividade de maior prioridade no Android e, como tal, só será morta pelo sistema operacional em situações extremas, como se a atividade tentar usar mais memória do que a disponível no dispositivo, pois isso pode fazer com que a interface do usuário pare de responder.
Pausado – Quando o dispositivo entra em modo de suspensão, ou uma atividade ainda está visível, mas parcialmente oculta por uma atividade nova, não de tamanho normal ou transparente, a atividade é considerada pausada. As atividades pausadas ainda estão ativas, ou seja, mantêm todas as informações de estado e de membros e permanecem ligadas ao gestor de janelas. Esta é considerada a segunda atividade de maior prioridade no Android e, como tal, só será morta pelo SO se matar esta atividade satisfizer os requisitos de recursos necessários para manter a atividade ativa/em execução estável e responsiva.
Parado/em segundo plano – Atividades que são completamente obscurecidas por outra atividade são consideradas interrompidas ou em segundo plano. As atividades interrompidas ainda tentam manter as suas informações de estado e membros pelo maior tempo possível, mas são consideradas a menor prioridade dos três estados e, como tal, o sistema operativo encerrará as atividades neste estado primeiro para satisfazer os requisitos de recursos das atividades de prioridade mais alta.
Reiniciado – É possível que uma atividade em qualquer estado, de pausada a parada no ciclo de vida, seja removida da memória pelo Android. Se o usuário navegar de volta para a atividade, ela deverá ser reiniciada, restaurada ao seu estado salvo anteriormente e, em seguida, exibida para o usuário.
Atividade Re-Creation em resposta a alterações de configuração
Para tornar as coisas mais complicadas, o Android introduz mais uma dificuldade chamada Alterações de Configuração. As alterações de configuração são ciclos rápidos de destruição/recriação de atividade que ocorrem quando a configuração de uma atividade muda, como quando o dispositivo é girado (e a atividade precisa ser reconstruída no modo paisagem ou retrato), quando o teclado é exibido (e a atividade é apresentada com uma oportunidade de se redimensionar) ou quando o dispositivo é colocado em um dock, entre outros.
As alterações de configuração ainda causam as mesmas alterações de estado de atividade que ocorreriam durante a interrupção e reinicialização de uma atividade. No entanto, para garantir que um aplicativo pareça responsivo e tenha um bom desempenho durante as alterações de configuração, é importante que elas sejam tratadas o mais rápido possível. Por isso, o Android tem uma API específica que pode ser usada para persistir o estado durante as alterações de configuração. Abordaremos isso mais adiante na seção Gerenciando o estado ao longo do ciclo de vida .
Métodos do ciclo de vida da atividade
O SDK do Android e, por extensão, a estrutura Xamarin.Android fornecem um modelo poderoso para gerenciar o estado das atividades dentro de um aplicativo. Quando o estado de uma atividade está mudando, a atividade é notificada pelo sistema operacional, que chama métodos específicos sobre essa atividade. O diagrama a seguir ilustra esses métodos em relação ao ciclo de vida da atividade:
Como desenvolvedor, você pode lidar com alterações de estado substituindo esses métodos em uma atividade. É importante notar, no entanto, que todos os métodos de ciclo de vida são chamados no thread da interface do usuário e bloquearão o sistema operacional de executar a próxima parte do trabalho da interface do usuário, como ocultar a atividade atual, exibir uma nova atividade, etc. Como tal, o código nesses métodos deve ser o mais breve possível para fazer com que um aplicativo se sinta bem executado. Todas as tarefas de longa execução devem ser executadas em um thread em segundo plano.
Vamos examinar cada um desses métodos de ciclo de vida e seu uso:
AoCriar
OnCreate é o primeiro método a ser chamado quando uma atividade é criada.
OnCreate é sempre sobrescrito para realizar quaisquer inicializações que possam ser exigidas por uma atividade, como:
- Criação de vistas
- Inicializando variáveis
- Vinculando dados estáticos a listas
OnCreate usa um parâmetro Bundle , que é um dicionário para armazenar e passar informações de estado e objetos entre atividades Se o bundle não for nulo, isso indica que a atividade está reiniciando e deve restaurar seu estado da instância anterior. O código a seguir ilustra como recuperar valores do pacote:
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
string intentString;
bool intentBool;
if (bundle != null)
{
intentString = bundle.GetString("myString");
intentBool = bundle.GetBoolean("myBool");
}
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.Main);
}
Uma vez OnCreate terminado, o Android chamará OnStart.
OnStart
O OnStart é sempre chamado pelo sistema depois de OnCreate estar concluído. As atividades podem substituir esse método se precisarem executar tarefas específicas antes que uma atividade se torne visível, como atualizar os valores atuais das exibições dentro da atividade. Android ligará OnResume imediatamente após este método.
OnResume
O sistema chama OnResume quando a atividade está pronta para começar a interagir com o usuário. As atividades devem substituir esse método para executar tarefas como:
- Aumentar as taxas de quadros (uma tarefa comum no desenvolvimento de jogos)
- Iniciando animações
- Escutando atualizações de GPS
- Exibir todos os alertas ou caixas de diálogo relevantes
- Conecte manipuladores de eventos externos
Como exemplo, o trecho de código a seguir mostra como inicializar a câmera:
protected override void OnResume()
{
base.OnResume(); // Always call the superclass first.
if (_camera==null)
{
// Do camera initializations here
}
}
OnResume é importante porque qualquer operação que é feita em OnPause deve ser desfeita em OnResume, uma vez que é o único método de ciclo de vida que é garantido que execute após OnPause ao trazer a atividade novamente à vida.
OnPause
OnPause é chamado quando o sistema está prestes a colocar a atividade em segundo plano ou quando a atividade fica parcialmente obscurecida. As atividades devem substituir esse método se precisarem:
Confirmar alterações não salvas em dados persistentes
Destrua ou limpe outros objetos que consomem recursos
Reduza gradualmente as taxas de quadros e interrompa animações
Cancele o registro de manipuladores de eventos externos ou manipuladores de notificação (ou seja, aqueles que estão vinculados a um serviço). Isso deve ser feito para evitar vazamentos de memória de atividade.
Da mesma forma, se a atividade tiver exibido quaisquer diálogos ou alertas, eles devem ser limpos com o
.Dismiss()método.
Como exemplo, o trecho de código a seguir liberará a câmera, pois a atividade não pode usá-la durante a pausa:
protected override void OnPause()
{
base.OnPause(); // Always call the superclass first
// Release the camera as other activities might need it
if (_camera != null)
{
_camera.Release();
_camera = null;
}
}
Há dois métodos de ciclo de vida possíveis que serão chamados após OnPause:
-
OnResumeserá chamada se a Atividade tiver de voltar ao primeiro plano. -
OnStopserá chamada se a Atividade estiver sendo colocada em segundo plano.
OnStop
OnStop é chamado quando a atividade não está mais visível para o usuário. Isso acontece quando ocorre uma das seguintes situações:
- Uma nova atividade está sendo iniciada e está encobrindo essa atividade.
- Uma atividade existente está sendo trazida para o primeiro plano.
- A atividade está sendo destruída.
OnStop nem sempre pode ser chamado em situações de pouca memória, como quando o Android está carente de recursos e não consegue colocar a Atividade em segundo plano adequadamente. Por esta razão, é melhor não confiar em OnStop ser chamado ao preparar uma Atividade para à destruição. Os próximos métodos de ciclo de vida que podem ser chamados após este serão OnDestroy se a Atividade estiver desaparecendo ou OnRestart se a Atividade estiver voltando para interagir com o usuário.
OnDestroy
OnDestroy é o método final que é chamado em uma instância Activity antes de ser destruído e completamente removido da memória. Em situações extremas, o Android pode matar o processo de aplicação que está hospedando a Atividade, o que resultará em OnDestroy não ser invocado. A maioria das atividades não implementará este método porque a maior parte da limpeza e do desligamento foi realizada nos métodos OnPause e OnStop. O OnDestroy método normalmente é substituído para limpar tarefas de longa execução que podem vazar recursos. Um exemplo disso são threads em segundo plano que foram iniciados no OnCreate.
Não haverá métodos do ciclo de vida invocados após a Atividade ter sido destruída.
OnRestart
OnRestart é chamado depois que sua atividade é interrompida, antes de ser iniciada novamente. Um bom exemplo disso seria quando o usuário pressiona o botão home durante uma atividade no aplicativo. Quando isso acontece OnPause e, em seguida OnStop , os métodos são chamados, e a atividade é movida para o plano de fundo, mas não é destruída. Se o usuário for restaurar o aplicativo usando o gerenciador de tarefas ou um aplicativo semelhante, o Android chamará o OnRestart método da atividade.
Não existem orientações gerais sobre o tipo de lógica que deve ser implementada no OnRestart. Isso ocorre porque OnStart é sempre invocado, independentemente de a Atividade estar sendo criada ou sendo reiniciada, portanto, todos os recursos exigidos pela Atividade devem ser inicializados em OnStart, em vez de OnRestart.
O próximo método de ciclo de vida chamado depois OnRestart será OnStart.
Voltar vs. Início
Muitos dispositivos Android têm dois botões distintos: um botão "Voltar" e um botão "Home". Um exemplo disso pode ser visto na seguinte captura de tela do Android 4.0.3:
Há uma diferença sutil entre os dois botões, embora eles pareçam ter o mesmo efeito de colocar um aplicativo em segundo plano. Quando um usuário clica no botão Voltar, ele está dizendo ao Android que terminou a atividade. O Android destruirá a Atividade. Em contraste, quando o usuário clica no botão Início a atividade é meramente colocada em segundo plano – o Android não matará a atividade.
Gerenciando o estado durante todo o ciclo de vida
Quando uma Atividade é interrompida ou destruída, o sistema oferece uma oportunidade de salvar o estado da Atividade para reidratação posterior. Este estado armazenado é conhecido como estado da instância. O Android fornece três opções para armazenar o estado da instância durante o ciclo de vida da atividade:
Armazenar valores primitivos em um
Dictionaryconhecido como um Bundle que o Android usará para salvar o estado.Criação de uma classe personalizada que conterá valores complexos, como bitmaps. O Android usará essa classe personalizada para salvar o estado.
Contornar o ciclo de vida da alteração de configuração e assumir total responsabilidade pela manutenção do estado na atividade.
Este guia abrange as duas primeiras opções.
Estado do pacote
A opção principal para salvar o estado da instância é usar um objeto de dicionário chave/valor conhecido como Bundle.
Lembre-se de que, quando uma atividade é criada, o OnCreate método recebe um pacote como parâmetro, esse pacote pode ser usado para restaurar o estado da instância. Não é recomendável usar um pacote para dados mais complexos que não serão serializados rápida ou facilmente para pares chave/valor (como bitmaps); em vez disso, ele deve ser usado para valores simples, como strings.
Uma atividade fornece métodos para ajudar a salvar e recuperar o estado da instância no pacote:
OnSaveInstanceState – Isso é invocado pelo Android quando a atividade está sendo destruída. As atividades podem implementar esse método se precisarem persistir quaisquer itens de estado de chave/valor.
OnRestoreInstanceState – Isso é chamado depois que o método é concluído e fornece outra oportunidade para uma atividade restaurar seu estado após a
OnCreateconclusão da inicialização.
O diagrama a seguir ilustra como esses métodos são usados:
OnSaveInstanceState
OnSaveInstanceState será chamado quando a atividade estiver sendo interrompida. Ele receberá um parâmetro de pacote no qual a atividade pode armazenar seu estado. Quando um dispositivo sofre uma alteração de configuração, uma Atividade pode usar o objeto Bundle que é passado para preservar o estado da Atividade sobrepondo OnSaveInstanceState. Por exemplo, considere o seguinte código:
int c;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
this.SetContentView (Resource.Layout.SimpleStateView);
var output = this.FindViewById<TextView> (Resource.Id.outputText);
if (bundle != null) {
c = bundle.GetInt ("counter", -1);
} else {
c = -1;
}
output.Text = c.ToString ();
var incrementCounter = this.FindViewById<Button> (Resource.Id.incrementCounter);
incrementCounter.Click += (s,e) => {
output.Text = (++c).ToString();
};
}
O código acima incrementa um inteiro chamado c quando um botão chamado incrementCounter é clicado, exibindo o resultado num TextView chamado output. Quando uma alteração de configuração acontece - por exemplo, quando o dispositivo é girado - o código acima perderia o valor de c porque o bundle seria null, como mostrado na figura abaixo:
Para preservar o valor de c neste exemplo, a Atividade pode substituir OnSaveInstanceState, salvando o valor no pacote conforme mostrado abaixo:
protected override void OnSaveInstanceState (Bundle outState)
{
outState.PutInt ("counter", c);
base.OnSaveInstanceState (outState);
}
Agora, quando o dispositivo é girado para uma nova orientação, o inteiro é salvo no pacote e é recuperado com a linha:
c = bundle.GetInt ("counter", -1);
Observação
É importante sempre chamar a implementação base de OnSaveInstanceState, para que o estado da hierarquia de visualização também possa ser salvo.
Estado de visualização
A sobrescrição OnSaveInstanceState é um mecanismo apropriado para salvar dados transitórios em uma Atividade durante as alterações de orientação, como o contador no exemplo acima. No entanto, a implementação padrão do OnSaveInstanceState cuidará de salvar dados transitórios na interface do usuário para cada exibição, desde que cada exibição tenha uma ID atribuída. Por exemplo, digamos que um aplicativo tenha um EditText elemento definido em XML da seguinte maneira:
<EditText android:id="@+id/myText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
Como o EditText controle tem um id atribuído, quando o usuário insere alguns dados e gira o dispositivo, os dados ainda são exibidos, como mostrado abaixo:
OnRestoreInstanceState
OnRestoreInstanceState será chamado após OnStart. Fornece a uma atividade a oportunidade de restaurar qualquer estado que foi salvo anteriormente em um Bundle durante o OnSaveInstanceState. Este é o mesmo pacote que é fornecido para OnCreate, no entanto.
O código a seguir demonstra como o estado pode ser restaurado em OnRestoreInstanceState:
protected override void OnRestoreInstanceState(Bundle savedState)
{
base.OnRestoreInstanceState(savedState);
var myString = savedState.GetString("myString");
var myBool = savedState.GetBoolean("myBool");
}
Este método existe para fornecer alguma flexibilidade em torno de quando o estado deve ser restaurado. Às vezes, é mais apropriado esperar até que todas as inicializações sejam feitas antes de restaurar o estado da instância. Além disso, uma subclasse de uma atividade existente pode querer restaurar apenas determinados valores do estado da instância. Em muitos casos, não é necessário substituir OnRestoreInstanceState, uma vez que a maioria das atividades pode restaurar o estado usando o pacote fornecido ao OnCreate.
Para obter um exemplo de como salvar o estado usando um Bundle, consulte o Passo a passo - Salvando o estado da atividade.
Limitações do pacote
Embora OnSaveInstanceState facilite o salvamento de dados transitórios, ele tem algumas limitações:
Não é chamado em todos os casos. Por exemplo, pressionar Início ou Voltar para sair de uma Atividade não resultará em
OnSaveInstanceStateser chamado.O pacote passado para dentro
OnSaveInstanceStatenão foi projetado para objetos grandes, como imagens. No caso de objetos grandes, salvar o objeto de OnRetainNonConfigurationInstance é preferível, conforme discutido abaixo.Os dados salvos usando o pacote são serializados, o que pode levar a atrasos.
O estado do pacote é útil para dados simples que não usam muita memória, enquanto os dados de instância sem configuração são úteis para dados mais complexos ou dados que são caros de recuperar, como de uma chamada de serviço Web ou uma consulta complicada de banco de dados. Os dados da instância que não são de configuração são guardados em um objeto quando necessário. A próxima seção apresenta OnRetainNonConfigurationInstance como uma forma de preservar tipos de dados mais complexos por meio de alterações de configuração.
Dados complexos persistentes
Além de persistir os dados no pacote, o Android também suporta salvar dados substituindo OnRetainNonConfigurationInstance e retornando uma instância de um Java.Lang.Object que contém os dados a serem persistidos. Há dois benefícios principais de usar OnRetainNonConfigurationInstance para salvar o estado:
O objeto retornado de
OnRetainNonConfigurationInstancetem um bom desempenho com tipos de dados maiores e mais complexos, pois é retido pela memória.O
OnRetainNonConfigurationInstancemétodo é chamado sob demanda e somente quando necessário. Isso é mais econômico do que usar um cache manual.
O uso OnRetainNonConfigurationInstance é adequado para cenários em que é caro recuperar os dados várias vezes, como em chamadas de serviço Web. Por exemplo, considere o seguinte código que pesquisa o Twitter:
public class NonConfigInstanceActivity : ListActivity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
SearchTwitter ("xamarin");
}
public void SearchTwitter (string text)
{
string searchUrl = String.Format("http://search.twitter.com/search.json?" + "q={0}&rpp=10&include_entities=false&" + "result_type=mixed", text);
var httpReq = (HttpWebRequest)HttpWebRequest.Create (new Uri (searchUrl));
httpReq.BeginGetResponse (new AsyncCallback (ResponseCallback), httpReq);
}
void ResponseCallback (IAsyncResult ar)
{
var httpReq = (HttpWebRequest)ar.AsyncState;
using (var httpRes = (HttpWebResponse)httpReq.EndGetResponse (ar)) {
ParseResults (httpRes);
}
}
void ParseResults (HttpWebResponse httpRes)
{
var s = httpRes.GetResponseStream ();
var j = (JsonObject)JsonObject.Load (s);
var results = (from result in (JsonArray)j ["results"] let jResult = result as JsonObject select jResult ["text"].ToString ()).ToArray ();
RunOnUiThread (() => {
PopulateTweetList (results);
});
}
void PopulateTweetList (string[] results)
{
ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
}
}
Esse código recupera resultados da Web formatados como JSON, analisa-os e, em seguida, apresenta os resultados em uma lista, conforme mostrado na captura de tela a seguir:
Quando ocorre uma alteração de configuração - por exemplo, quando um dispositivo é girado - o código repete o processo. Para reutilizar os resultados originalmente recuperados e não causar chamadas de rede desnecessárias e redundantes, podemos usar OnRetainNonconfigurationInstance para salvar os resultados, como mostrado abaixo:
public class NonConfigInstanceActivity : ListActivity
{
TweetListWrapper _savedInstance;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
var tweetsWrapper = LastNonConfigurationInstance as TweetListWrapper;
if (tweetsWrapper != null) {
PopulateTweetList (tweetsWrapper.Tweets);
} else {
SearchTwitter ("xamarin");
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
return _savedInstance;
}
...
void PopulateTweetList (string[] results)
{
ListAdapter = new ArrayAdapter<string> (this, Resource.Layout.ItemView, results);
_savedInstance = new TweetListWrapper{Tweets=results};
}
}
Agora quando o dispositivo é girado, os resultados originais são recuperados da propriedade LastNonConfiguartionInstance. Neste exemplo, os resultados consistem em um(a) string[] que contém tweets. Uma vez que OnRetainNonConfigurationInstance requer que um Java.Lang.Object seja retornado, o string[] é envolvido em uma classe que subclasse Java.Lang.Object, como se mostra abaixo.
class TweetListWrapper : Java.Lang.Object
{
public string[] Tweets { get; set; }
}
Por exemplo, tentar usar uma TextView como o objeto retornado por OnRetainNonConfigurationInstance fará com que a Atividade vaze, conforme ilustrado pelo código abaixo:
TextView _textView;
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
var tv = LastNonConfigurationInstance as TextViewWrapper;
if(tv != null) {
_textView = tv;
var parent = _textView.Parent as FrameLayout;
parent.RemoveView(_textView);
} else {
_textView = new TextView (this);
_textView.Text = "This will leak.";
}
SetContentView (_textView);
}
public override Java.Lang.Object OnRetainNonConfigurationInstance ()
{
base.OnRetainNonConfigurationInstance ();
return _textView;
}
Nesta secção, aprendemos como preservar dados de estado simples com o Bundle, e persistir tipos de dados mais complexos com o OnRetainNonConfigurationInstance.
Resumo
O ciclo de vida da atividade do Android fornece uma estrutura poderosa para o gerenciamento de estado de atividades dentro de um aplicativo, mas pode ser complicado de entender e implementar. Este capítulo apresentou os diferentes estados pelos quais uma atividade pode passar durante sua vida, bem como os métodos de ciclo de vida associados a esses estados. Em seguida, foram fornecidas orientações sobre que tipo de lógica deve ser realizada em cada um desses métodos.