Eu tenho trabalhado na plataforma Android SDK, e não está claro como salvar o estado de um aplicativo. Portanto, considerando esta pequena reformulação do exemplo 'Olá, Android':
package com.android.hello;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class HelloAndroid extends Activity {
private TextView mTextView = null;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextView = new TextView(this);
if (savedInstanceState == null) {
mTextView.setText("Welcome to HelloAndroid!");
} else {
mTextView.setText("Welcome back.");
}
setContentView(mTextView);
}
}
Eu pensei que seria o suficiente para o caso mais simples, mas ele sempre responde com a primeira mensagem, independentemente de como eu me afasto do aplicativo.
Tenho certeza de que a solução é tão simples quanto substituir onPause
ou algo parecido, mas estou procurando na documentação há cerca de 30 minutos e não encontrei nada óbvio.
Respostas:
Você precisa substituir
onSaveInstanceState(Bundle savedInstanceState)
e gravar os valores do estado do aplicativo que deseja alterar para oBundle
parâmetro desta forma:O Bundle é essencialmente uma forma de armazenar um mapa NVP ( "par nome-valor"), e ele vai ter passado para
onCreate()
e tambémonRestoreInstanceState()
onde você iria em seguida, extrair os valores de actividade como este:Ou de um fragmento.
Você usaria essa técnica para armazenar valores de instância para seu aplicativo (seleções, texto não salvo, etc.).
fonte
onSaveInstanceState
quase inútil, exceto apenas no caso de alterações na orientação da tela. Em quase todos os outros casos, você nunca pode confiar nele e precisará salvar manualmente o estado da interface do usuário em outro lugar. Ou impedir que seu aplicativo seja eliminado, substituindo o comportamento do botão VOLTAR. Não entendo por que eles o implementaram dessa maneira em primeiro lugar. Totalmente não intuitivo. E você não pode ter esse pacote que o sistema permite salvar, exceto neste método muito específico.View
s que foram ids atribuídas . DosonSaveInstanceState
documentos: "A implementação padrão cuida da maior parte do estado da interface do usuário por instância, chamandoonSaveInstanceState()
cada exibição na hierarquia que possui um ID e salvando o ID da exibição atualmente focada (tudo restaurado pela implementação padrão deonRestoreInstanceState(Bundle)
) "O
savedInstanceState
é apenas para salvar o estado associado a uma instância atual de uma Atividade, por exemplo, informações atuais de navegação ou seleção, para que, se o Android destruir e recriar uma Atividade, ele possa voltar como antes. Consulte a documentação paraonCreate
eonSaveInstanceState
Para um estado de vida mais longa, considere usar um banco de dados SQLite, um arquivo ou preferências. Consulte Salvando estado persistente .
fonte
Observe que NÃO é seguro usar
onSaveInstanceState
eonRestoreInstanceState
para dados persistentes , de acordo com a documentação dos estados de Atividade em http://developer.android.com/reference/android/app/Activity.html .O documento declara (na seção 'Ciclo de vida da atividade'):
Em outras palavras, coloque seu código de salvar / restaurar dados persistentes em
onPause()
eonResume()
!EDIT : Para mais esclarecimentos, aqui está a
onSaveInstanceState()
documentação:fonte
O meu colega escreveu um artigo explicando o estado do aplicativo em dispositivos Android, incluindo explicações sobre o ciclo de vida de atividade e informações de estado, como armazenar informações de estado, e economizando para o estado
Bundle
eSharedPreferences
e dar uma olhada aqui .O artigo aborda três abordagens:
Armazene dados de controle da variável local / interface do usuário durante a vida útil do aplicativo (ou seja, temporariamente) usando um pacote de estado da instância
Armazene dados de controle de variável / interface local entre instâncias de aplicativos (ou seja, permanentemente) usando preferências compartilhadas
Manter instâncias de objetos ativas na memória entre as atividades durante a vida útil do aplicativo usando uma instância de não configuração retida
fonte
Este é um 'truque' clássico do desenvolvimento do Android. Há duas questões aqui:
Navegando por todos esses tópicos, suspeito que na maioria das vezes os desenvolvedores estão falando sobre esses dois problemas diferentes simultaneamente ... portanto, toda a confusão e os relatórios de "isso não funciona para mim".
Primeiro, para esclarecer o comportamento 'pretendido': onSaveInstance e onRestoreInstance são frágeis e apenas para o estado transitório. O uso pretendido (proibido) é lidar com a recreação de atividades quando o telefone é girado (mudança de orientação). Em outras palavras, o uso pretendido é quando sua Atividade ainda está logicamente 'no topo', mas ainda deve ser reintegrada pelo sistema. O Bundle salvo não é mantido fora do processo / memória / gc, portanto você não pode confiar nisso se sua atividade for em segundo plano. Sim, talvez a memória da sua atividade sobreviva à sua viagem ao plano de fundo e escape do GC, mas isso não é confiável (nem previsível).
Portanto, se você tiver um cenário em que haja um 'progresso do usuário' ou estado significativo que deva persistir entre os 'lançamentos' do seu aplicativo, a orientação é usar onPause e onResume. Você deve escolher e preparar uma loja persistente.
MAS - existe um bug muito confuso que complica tudo isso. Os detalhes estão aqui:
http://code.google.com/p/android/issues/detail?id=2373
http://code.google.com/p/android/issues/detail?id=5277
Basicamente, se seu aplicativo for iniciado com o sinalizador SingleTask e, posteriormente, você o iniciar na tela inicial ou no menu do iniciador, essa chamada subsequente criará uma NOVA tarefa ... você terá duas instâncias diferentes do seu aplicativo habitando a mesma pilha ... o que fica muito estranho muito rápido. Isso parece acontecer quando você inicia o aplicativo durante o desenvolvimento (por exemplo, do Eclipse ou do Intellij), então os desenvolvedores se deparam muito com isso. Mas também através de alguns dos mecanismos de atualização da loja de aplicativos (que também afeta seus usuários).
Eu lutei com esses threads por horas antes de perceber que meu principal problema era esse bug, não o comportamento pretendido da estrutura. Uma excelente redação e
Gambiarra(ATUALIZAÇÃO: veja abaixo) parece ser do usuário @kaciula nesta resposta:Comportamento ao pressionar a tecla Home
ATUALIZAÇÃO junho de 2013 : meses depois, finalmente encontrei a solução 'correta'. Você não precisa gerenciar nenhum sinalizador iniciado do StateApp, pode detectar isso a partir da estrutura e resgatar adequadamente. Eu uso isso no início do meu LauncherActivity.onCreate:
fonte
onSaveInstanceState
é chamado quando o sistema precisa de memória e mata um aplicativo. Não é chamado quando o usuário apenas fecha o aplicativo. Acho que o estado do aplicativo também deve ser salvo emonPause
Deve ser salvo em algum armazenamento persistente, comoPreferences
ouSqlite
fonte
Ambos os métodos são úteis e válidos e ambos são mais adequados para diferentes cenários:
onSaveInstanceState()
eonRestoreInstanceState()
geralmente é adequado.Se você salvar os dados do estado de maneira persistente, eles poderão ser recarregados em uma
onResume()
ouonCreate()
(ou realmente em qualquer chamada do ciclo de vida). Esse pode ou não ser o comportamento desejado. Se você armazená-lo em um pacote configurável em umInstanceState
, ele é transitório e é adequado apenas para armazenar dados para uso na mesma 'sessão' do usuário (eu uso o termo sessão livremente), mas não entre 'sessões'.Não é que uma abordagem seja melhor que a outra, como tudo, é apenas importante entender qual comportamento você precisa e selecionar a abordagem mais apropriada.
fonte
O estado de economia é, na melhor das hipóteses, um desentendimento. Se você precisar salvar dados persistentes, basta usar um banco de dados SQLite . O Android torna MUITO fácil.
Algo assim:
Uma simples ligação depois disso
fonte
Eu acho que encontrei a resposta. Deixe-me contar o que fiz em palavras simples:
Suponha que eu tenha duas atividades, atividade1 e atividade2, e estou navegando da atividade1 para a atividade2 (fiz alguns trabalhos na atividade2) e novamente para a atividade 1 clicando em um botão na atividade1. Agora, nesse estágio, eu queria voltar à atividade2 e quero ver minha atividade2 na mesma condição em que saí pela última vez da atividade2.
Para o cenário acima, o que fiz foi que, no manifesto, fiz algumas alterações como esta:
E na atividade1 no botão, clique no evento que eu fiz assim:
E na atividade2 no evento de clique no botão, eu fiz assim:
Agora, o que acontecerá é que, quaisquer que sejam as alterações que fizemos na atividade2, não serão perdidas e podemos visualizar a atividade2 no mesmo estado em que saímos anteriormente.
Eu acredito que esta é a resposta e isso funciona bem para mim. Corrija-me se eu estiver enganado.
fonte
onSaveInstanceState()
para dados transitórios (restaurados emonCreate()
/onRestoreInstanceState()
),onPause()
para dados persistentes (restaurados emonResume()
). Dos recursos técnicos do Android:fonte
Realmente
onSaveInstanceState()
é chamado quando a Atividade vai para segundo plano.Cite os documentos: "Este método é chamado antes que uma atividade possa ser eliminada, para que, quando voltar no futuro, possa restaurar seu estado". Fonte
fonte
Para ajudar a reduzir o boilerplate, use o seguinte
interface
eclass
para ler / gravar em umBundle
para salvar o estado da instância.Primeiro, crie uma interface que será usada para anotar suas variáveis de instância:
Em seguida, crie uma classe em que a reflexão será usada para salvar valores no pacote configurável:
Exemplo de uso:
Nota: Este código foi adaptado de um projeto de biblioteca chamado AndroidAutowire, licenciado sob a licença MIT .
fonte
Enquanto isso, eu geralmente não uso mais
O ciclo de vida é para a maioria das atividades muito complicado e desnecessário.
E o Google afirma que ele NÃO é confiável.
Minha maneira é salvar as alterações imediatamente nas preferências:
De alguma forma, SharedPreferences funcionam de forma semelhante aos Bundles. E, naturalmente, a princípio, esses valores precisam ser lidos nas preferências.
No caso de dados complexos, você pode usar SQLite em vez de usar preferências.
Ao aplicar esse conceito, a atividade continua a usar o último estado salvo, independentemente de ter sido uma abertura inicial com reinicializações intermediárias ou uma reabertura devido à pilha de trás.
fonte
Para responder diretamente à pergunta original. savedInstancestate é nulo porque sua Atividade nunca está sendo recriada.
Sua atividade será recriada apenas com um pacote de estado quando:
O Android destruirá as atividades em segundo plano quando estiver sob pressão da memória ou depois de ficarem em segundo plano por um longo período de tempo.
Ao testar o seu exemplo de olá mundo, existem algumas maneiras de sair e retornar à Atividade.
Na maioria dos casos, se você estiver apenas pressionando a tecla Início e, em seguida, iniciando o aplicativo novamente, a atividade não precisará ser recriada. Ele já existe na memória e onCreate () não será chamado.
Há uma opção em Configurações -> Opções do desenvolvedor, chamada "Não manter atividades". Quando ativado, o Android sempre destrói atividades e as recria quando estão em segundo plano. Essa é uma ótima opção para deixar ativada no desenvolvimento, pois simula o pior cenário possível. (Um dispositivo com pouca memória reciclando suas atividades o tempo todo).
As outras respostas são valiosas, pois ensinam as maneiras corretas de armazenar o estado, mas não achei que elas realmente respondessem POR QUE seu código não estava funcionando da maneira que você esperava.
fonte
Os métodos
onSaveInstanceState(bundle)
eonRestoreInstanceState(bundle)
são úteis para a persistência dos dados apenas durante a rotação da tela (alteração de orientação).Eles não são ainda bem enquanto alternar entre aplicações (uma vez que o
onSaveInstanceState()
método é chamado, masonCreate(bundle)
eonRestoreInstanceState(bundle)
não é invocado novamente.Para mais preferências uso compartilhado persistência. Ler este artigo
fonte
onCreate
eonRestoreInstanceState
não está sendo chamado porque eleActivity
não é destruído quando você alterna entre aplicativos, portanto, não há necessidade de restaurar nada. O Android chamaonSaveInstanceState
apenas no caso de a Atividade ser destruída mais tarde (o que acontece com 100% de certeza ao girar a tela, porque toda a configuração do dispositivo foi alterada e a Atividade deve ser recriada do zero).Meu problema era que eu só precisava de persistência durante a vida útil do aplicativo (ou seja, uma única execução incluindo iniciar outras sub-atividades no mesmo aplicativo e girar o dispositivo etc.). Tentei várias combinações das respostas acima, mas não consegui o que queria em todas as situações. No final, o que funcionou para mim foi obter uma referência ao savedInstanceState durante onCreate:
e use isso para obter o conteúdo da minha variável quando eu precisar, nas linhas de:
Eu uso
onSaveInstanceState
eonRestoreInstanceState
como sugerido acima, mas acho que eu também poderia ou alternativamente usar meu método para salvar a variável quando ela mudar (por exemplo, usandoputBoolean
)fonte
Embora a resposta aceita esteja correta, existe um método mais rápido e fácil de salvar o estado de Atividade no Android usando uma biblioteca chamada Icepick . Icepick é um processador de anotação que cuida de todo o código padrão usado para salvar e restaurar o estado para você.
Fazendo algo parecido com isto com Icepick:
É o mesmo que fazer isso:
O Icepick funcionará com qualquer objeto que salvar seu estado com a
Bundle
.fonte
Quando uma atividade é criada, o método onCreate () é chamado.
savedInstanceState é um objeto da classe Bundle que é nulo pela primeira vez, mas contém valores quando é recriado. Para salvar o estado da atividade, você deve substituir onSaveInstanceState ().
coloque seus valores em "outState" Objeto do pacote como outState.putString ("key", "Welcome Back") e salve chamando super. Quando a atividade é destruída, seu estado é salvo no objeto Bundle e pode ser restaurado após a recreação em onCreate () ou onRestoreInstanceState (). O pacote recebido em onCreate () e onRestoreInstanceState () é o mesmo.
ou
fonte
Existem basicamente duas maneiras de implementar essa mudança.
onSaveInstanceState()
eonRestoreInstanceState()
.android:configChanges="orientation|screenSize"
.Eu realmente não recomendo usar o segundo método. Como em uma das minhas experiências, ele causou a metade da tela do dispositivo ficar preta ao girar de retrato para paisagem e vice-versa.
Usando o primeiro método mencionado acima, podemos manter os dados quando a orientação é alterada ou qualquer alteração na configuração acontece. Eu conheço uma maneira pela qual você pode armazenar qualquer tipo de dados dentro do objeto de estado SavedInstance.
Exemplo: considere um caso se desejar persistir o objeto Json. crie uma classe de modelo com getters e setters.
Agora, na sua atividade nos métodos onCreate e onSaveInstanceState, faça o seguinte. Vai parecer algo assim:
fonte
Aqui está um comentário da resposta de Steve Moseley (por ToolmakerSteve ) que coloca as coisas em perspectiva (no conjunto onSaveInstanceState vs onPause, custo leste versus saga custo oeste)
fonte
Código Kotlin:
Salve :
e depois em
onCreate()
ouonRestoreInstanceState()
Adicione valores padrão se você não quiser ter opcionais
fonte
Para obter os dados do estado da atividade armazenados
onCreate()
, primeiro você deve salvar os dados em savedInstanceState peloSaveInstanceState(Bundle savedInstanceState)
método de substituição .Quando o
SaveInstanceState(Bundle savedInstanceState)
método de destruição da atividade é chamado e você salva os dados que deseja salvar. E você obtém o mesmoonCreate()
quando a atividade é reiniciada. (SavedInstanceState não será nulo, pois você salvou alguns dados antes da atividade ser destruída)fonte
Simples e rápido para resolver este problema está usando o IcePick
Primeiro, configure a biblioteca em
app/build.gradle
Agora, vamos verificar este exemplo abaixo como salvar o estado em Activity
Ele funciona para atividades, fragmentos ou qualquer objeto que precise serializar seu estado em um pacote (por exemplo, ViewPresenters da argamassa)
O Icepick também pode gerar o código de estado da instância para Visualizações personalizadas:
fonte
Não tenho certeza se minha solução é desaprovada ou não, mas eu uso um serviço vinculado para persistir o estado do ViewModel. Se você armazená-lo na memória no serviço ou persistir e recuperá-lo de um banco de dados SQLite, depende de seus requisitos. É isso que serviços de qualquer tipo fazem: eles fornecem serviços como manter o estado do aplicativo e abstrair a lógica comercial comum.
Devido a restrições de memória e processamento inerentes a dispositivos móveis, trato as visualizações do Android de maneira semelhante a uma página da web. A página não mantém o estado, é puramente um componente da camada de apresentação cujo único objetivo é apresentar o estado do aplicativo e aceitar a entrada do usuário. As tendências recentes na arquitetura de aplicativos da web empregam o uso do padrão Modelo, Visualização, Controlador (MVC), onde a página é a Visualização, os dados do domínio são o modelo e o controlador fica atrás de um serviço da Web. O mesmo padrão pode ser empregado no Android, com o View sendo, bem ... o View, o modelo são os dados do seu domínio e o Controller é implementado como um serviço vinculado ao Android. Sempre que você desejar que uma visualização interaja com o controlador, vincule-a ao iniciar / retomar e desenrole ao parar / pausar.
Essa abordagem oferece a você o bônus adicional de impor o princípio de design Separação de Preocupação, em que todas as lógicas de negócios de aplicativos podem ser movidas para o seu serviço, o que reduz a lógica duplicada em várias visualizações e permite que a visualização imponha outro importante princípio de design, Responsabilidade Única.
fonte
Kotlin
Você deve substituir
onSaveInstanceState
eonRestoreInstanceState
armazenar e recuperar suas variáveis que deseja que sejam persistentesGráfico do ciclo de vida
Armazenar variáveis
Recuperar variáveis
fonte
Agora, o Android fornece ViewModels para salvar o estado, você deve tentar usá-lo em vez de saveInstanceState.
fonte
Existe uma maneira de fazer o Android salvar os estados sem implementar nenhum método. Basta adicionar esta linha à sua declaração de Manifesto em Atividade:
Deve ficar assim:
Aqui você pode encontrar mais informações sobre essa propriedade.
É recomendável deixar que o Android lide com isso para você do que com o manuseio manual.
fonte
O que salvar e o que não salvar?
Já se perguntou por que o texto no
EditText
é salvo automaticamente enquanto uma orientação muda? Bem, esta resposta é para você.Quando uma instância de uma Atividade é destruída e o Sistema recria uma nova instância (por exemplo, alteração na configuração). Ele tenta recriá-lo usando um conjunto de dados salvos do antigo Activity Activity ( estado da instância ).
O estado da instância é uma coleção de pares de valores-chave armazenados em um
Bundle
objeto.EditText
ListView
, etc.Se você precisar que outra variável seja salva como parte do estado da instância, você deverá substituir o
onSavedInstanceState(Bundle savedinstaneState)
método.Por exemplo,
int currentScore
em um GameActivityMais detalhes sobre o onSavedInstanceState (Bundle savedinstaneState) ao salvar dados
Qual escolher para restaurar o estado da atividade?
OU
Ambos os métodos obtêm o mesmo objeto Bundle, portanto, não importa onde você escreve sua lógica de restauração. A única diferença é que, no
onCreate(Bundle savedInstanceState)
método, você terá que fazer uma verificação nula enquanto não for necessária no último caso. Outras respostas já possuem trechos de código. Você pode encaminhá-los.Mais detalhes sobre o onRestoreInstanceState (Bundle savedinstaneState)
Bônus
O
onSaveInstanceState(Bundle savedInstanceState)
é invocado pelo sistema somente quando o usuário pretende retornar à Atividade. Por exemplo, você está usando o App X e, de repente, recebe uma ligação. Você passa para o aplicativo chamador e volta para o aplicativo X. Nesse caso, oonSaveInstanceState(Bundle savedInstanceState)
método será chamado.Mas considere isso se um usuário pressionar o botão Voltar. Supõe-se que o usuário não pretenda voltar à Atividade, portanto, neste caso
onSaveInstanceState(Bundle savedInstanceState)
, não será invocado pelo sistema. Aponte que você deve considerar todos os cenários ao salvar dados.Links relevantes:
Demonstração do comportamento padrão da
documentação oficial do Android .
fonte
Agora faz sentido fazer duas maneiras no modelo de exibição. se você deseja salvar a primeira como uma instância salva: é possível adicionar o parâmetro state no modelo de exibição como este https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#java
ou você pode salvar variáveis ou objetos no modelo de vista, nesse caso, o modelo de vista manterá o ciclo de vida até que a atividade seja destruída.
fonte
você pode usar o
Live Data
eView Model
para Lifecycle Handel
deJetPack
. veja esta referência:https://developer.android.com/topic/libraries/architecture/livedata
fonte