Como salvar um estado de atividade usando o estado da instância de salvamento?

2620

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 onPauseou algo parecido, mas estou procurando na documentação há cerca de 30 minutos e não encontrei nada óbvio.

Bernard
fonte
9
Quando é salvoInstanceState == null e quando não é nulo?
Trojan.ZBOT
90
Você está destruindo explicitamente sua atividade - como disse, navegando para longe dela, como pressionando para trás. Na verdade, o cenário em que esse 'savedInstanceState' é usado é quando o Android destrói sua atividade para recreação. Por exemplo: se você alterar o idioma do seu telefone enquanto a atividade estava em execução (é necessário carregar recursos diferentes do seu projeto). Outro cenário muito comum é quando você gira o telefone para o lado para que a atividade seja recriada e exibida na paisagem.
villoren
16
Para receber a segunda mensagem, habilite "Não manter atividades" nas opções de desenvolvimento. Pressione um botão de página inicial e volte dos recentes.
Yaroslav Mytkalyk
5
isso é bastante útil developer.android.com/training/basics/activity-lifecycle/...
Syed Raza Mehdi
6
você pode fazer isso com: onSaveInstanceState (Bundle savedInstanceState)
VahidHoseini 4/16/16

Respostas:

2568

Você precisa substituir onSaveInstanceState(Bundle savedInstanceState)e gravar os valores do estado do aplicativo que deseja alterar para o Bundleparâmetro desta forma:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
  super.onSaveInstanceState(savedInstanceState);
  // Save UI state changes to the savedInstanceState.
  // This bundle will be passed to onCreate if the process is
  // killed and restarted.
  savedInstanceState.putBoolean("MyBoolean", true);
  savedInstanceState.putDouble("myDouble", 1.9);
  savedInstanceState.putInt("MyInt", 1);
  savedInstanceState.putString("MyString", "Welcome back to Android");
  // etc.
}

O Bundle é essencialmente uma forma de armazenar um mapa NVP ( "par nome-valor"), e ele vai ter passado para onCreate()e também onRestoreInstanceState()onde você iria em seguida, extrair os valores de actividade como este:

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
  super.onRestoreInstanceState(savedInstanceState);
  // Restore UI state from the savedInstanceState.
  // This bundle has also been passed to onCreate.
  boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
  double myDouble = savedInstanceState.getDouble("myDouble");
  int myInt = savedInstanceState.getInt("MyInt");
  String myString = savedInstanceState.getString("MyString");
}

Ou de um fragmento.

@Override
public void onViewStateRestored(@Nullable Bundle savedInstanceState) {
    super.onViewStateRestored(savedInstanceState);
    // Restore UI state from the savedInstanceState.
    // This bundle has also been passed to onCreate.
    boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
    double myDouble = savedInstanceState.getDouble("myDouble");
    int myInt = savedInstanceState.getInt("MyInt");
    String myString = savedInstanceState.getString("MyString");
}

Você usaria essa técnica para armazenar valores de instância para seu aplicativo (seleções, texto não salvo, etc.).

Reto Meier
fonte
24
Alguma chance de isso funcionar no telefone, mas não no emulador? Não consigo obter um saveInstanceState não nulo.
Adam Jack
491
CUIDADO: você precisa chamar super.onSaveInstanceState (savedInstanceState) antes de adicionar seus valores ao Bundle, ou eles serão eliminados nessa chamada (Droid X Android 2.2).
precisa saber é o seguinte
121
Cuidado: a documentação oficial declara que você deve salvar informações importantes no método onPause, porque o método onsaveinstance não faz parte do ciclo de vida do Android. developer.android.com/reference/android/app/Activity.html
schlingel
32
Esse fato efetivamente torna onSaveInstanceStatequase 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.
chakrit
12
Note-se que save / restore estado UI de / para o Bundle é automaticamente tomado cuidado para Views que foram ids atribuídas . Dos onSaveInstanceStatedocumentos: "A implementação padrão cuida da maior parte do estado da interface do usuário por instância, chamando onSaveInstanceState()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 de onRestoreInstanceState(Bundle)) "
Vicky Chijwani
433

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 para onCreateeonSaveInstanceState

Para um estado de vida mais longa, considere usar um banco de dados SQLite, um arquivo ou preferências. Consulte Salvando estado persistente .

Dave L.
fonte
3
Quando é salvoInstanceState == null e quando não é nulo?
seguida
6
savedInstanceState é nulo quando o sistema está criando uma nova instância da sua Atividade e não nulo quando está restaurando.
Gabriel Câmara
7
... o que levanta a questão de quando o sistema precisa criar uma nova instância da Activity. Algumas maneiras de sair de um aplicativo não criam um pacote, portanto, uma nova instância deve ser criada. Esse é o problema fundamental; significa que não se pode confiar na existência de pacote configurável e deve-se usar alguns meios alternativos de armazenamento persistente. O benefício do onSave / onRestoreInstanceState é que é um mecanismo que o sistema pode executar abruptamente , sem consumir muitos recursos do sistema. Portanto, é bom oferecer suporte a isso, além de ter armazenamento persistente para uma saída mais tranquila do aplicativo.
Página
415

Observe que NÃO é seguro usar onSaveInstanceStatee onRestoreInstanceState 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'):

Observe que é importante salvar dados persistentes em onPause()vez de onSaveInstanceState(Bundle) porque os últimos não fazem parte dos retornos de chamada do ciclo de vida, portanto, não serão chamados em todas as situações, conforme descrito em sua documentação.

Em outras palavras, coloque seu código de salvar / restaurar dados persistentes em onPause()e onResume()!

EDIT : Para mais esclarecimentos, aqui está a onSaveInstanceState()documentação:

Esse método é chamado antes que uma atividade possa ser eliminada, para que, quando voltar algum tempo no futuro, possa restaurar seu estado. Por exemplo, se a atividade B for iniciada na frente da atividade A e, em algum momento, a atividade A for interrompida para recuperar recursos, a atividade A terá a chance de salvar o estado atual de sua interface com o usuário por meio desse método, para que, quando o usuário retornar para a atividade A, o estado da interface do usuário pode ser restaurado via onCreate(Bundle)ou onRestoreInstanceState(Bundle).

Steve Moseley
fonte
55
Apenas para escolher: também não é perigoso. Isso depende apenas do que você deseja preservar e por quanto tempo, o que @Bernard não está totalmente claro em sua pergunta original. InstanceState é perfeito para preservar o estado atual da interface do usuário (dados inseridos em controles, posições atuais em listas e assim por diante), enquanto Pausar / Continuar é a única possibilidade de armazenamento persistente a longo prazo.
Pontus Gagge
30
Isso deve ser diminuído. Não é seguro usar no InstanceState (Salvar | Restaurar) como métodos de ciclo de vida (por exemplo, faça qualquer outra coisa além de salvar / restaurar o estado). Eles são perfeitamente bons para salvar / restaurar o estado. Além disso, como você deseja salvar / restaurar o estado no onPause e noResume? Você não obtém Bundles nos métodos que pode usar; portanto, é necessário empregar outros tipos de economia de estado, em bancos de dados, arquivos etc., o que é estúpido.
Felix
141
Não devemos votar negativamente nesta pessoa, pelo menos ele fez esforços para revisar a documentação e acho que estamos aqui para realmente construir uma comunidade experiente e ajudar uns aos outros a não votar em baixa. então votei no esforço e pedirei que as pessoas não votem menos ou votem ... essa pessoa limpa a confusão que você gostaria de ter ao passar pela documentação. 1 voto
positivo
21
Não acho que essa resposta mereça voto negativo. Pelo menos ele fez um esforço para responder e citou uma seção de doco.
GSree
34
Esta resposta é absolutamente correta e merece voto UP, não abaixo! Deixe-me esclarecer a diferença entre estados para aqueles que não a veem. Um estado da GUI, como botões de opção selecionados e algum texto no campo de entrada, é muito menos importante que o estado dos dados, como registros adicionados a uma lista exibida em um ListView. O último deve ser armazenado no banco de dados no onPause, porque é a única chamada garantida. Se você colocá-lo em onSaveInstanceState, corre o risco de perder dados se isso não for chamado. Mas se a seleção do botão de opção não for salva pelo mesmo motivo - não é grande coisa.
JBM
206

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 Bundlee SharedPreferencese 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

[Code sample  Store state in state bundle]
@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
  // Store UI state to the savedInstanceState.
  // This bundle will be passed to onCreate on next call.  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  savedInstanceState.putString(“Name”, strName);
  savedInstanceState.putString(“Email”, strEmail);
  savedInstanceState.putBoolean(“TandC”, blnTandC);

  super.onSaveInstanceState(savedInstanceState);
}

Armazene dados de controle de variável / interface local entre instâncias de aplicativos (ou seja, permanentemente) usando preferências compartilhadas

[Code sample  store state in SharedPreferences]
@Override
protected void onPause()
{
  super.onPause();

  // Store values between instances here
  SharedPreferences preferences = getPreferences(MODE_PRIVATE);
  SharedPreferences.Editor editor = preferences.edit();  // Put the values from the UI
  EditText txtName = (EditText)findViewById(R.id.txtName);
  String strName = txtName.getText().toString();

  EditText txtEmail = (EditText)findViewById(R.id.txtEmail);
  String strEmail = txtEmail.getText().toString();

  CheckBox chkTandC = (CheckBox)findViewById(R.id.chkTandC);
  boolean blnTandC = chkTandC.isChecked();

  editor.putString(“Name”, strName); // value to store
  editor.putString(“Email”, strEmail); // value to store
  editor.putBoolean(“TandC”, blnTandC); // value to store
  // Commit to storage
  editor.commit();
}

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

[Code sample  store object instance]
private cMyClassType moInstanceOfAClass; // Store the instance of an object
@Override
public Object onRetainNonConfigurationInstance()
{
  if (moInstanceOfAClass != null) // Check that the object exists
      return(moInstanceOfAClass);
  return super.onRetainNonConfigurationInstance();
}
Martin Belcher - AtWrk
fonte
3
@ MartinBelcher-Eigo O artigo diz sobre os dados no SharedPreferences que "Esses dados são gravados no banco de dados no dispositivo .." Eu acredito que os dados são armazenados em um arquivo no diretório do sistema de arquivos do aplicativo.
26412 Tom
2
Os dados do @Tom SharefPrefs são gravados no arquivo xml. O xml é um tipo de banco de dados? Eu diria que é;)
MaciejGórski 2/13/13
148

Este é um 'truque' clássico do desenvolvimento do Android. Há duas questões aqui:

  • Há um bug sutil do Android Framework que complica muito o gerenciamento de pilhas de aplicativos durante o desenvolvimento, pelo menos nas versões herdadas (não totalmente certo se / quando / como foi corrigido). Vou discutir este bug abaixo.
  • A maneira 'normal' ou planejada de gerenciar esse problema é, por si só, bastante complicada com a dualidade de onPause / onResume e onSaveInstanceState / onRestoreInstanceState

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 eGambiarra (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:

if (!isTaskRoot()) {
    Intent intent = getIntent();
    String action = intent.getAction();
    if (intent.hasCategory(Intent.CATEGORY_LAUNCHER) && action != null && action.equals(Intent.ACTION_MAIN)) {
        finish();
        return;
    }
}
Mike Repass
fonte
87

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 em onPauseDeve ser salvo em algum armazenamento persistente, como PreferencesouSqlite

Fedor
fonte
36
Desculpe, isso não está correto. onSaveInstanceState é chamado antes que a atividade precise ser refeita. ou seja, toda vez que o usuário gira o dispositivo. Destina-se ao armazenamento de estados de exibição transitórios. Quando o Android força o aplicativo a fechar, onSaveInstanceState NÃO é chamado (é por isso que não é seguro armazenar dados importantes do aplicativo). onPause, no entanto, é garantido que ele será chamado antes que a atividade seja encerrada, portanto, ela deve ser usada para armazenar informações permanentes nas preferências ou no Squlite. Resposta certa, razões erradas.
usar o seguinte
74

Ambos os métodos são úteis e válidos e ambos são mais adequados para diferentes cenários:

  1. O usuário encerra o aplicativo e o reabre posteriormente, mas o aplicativo precisa recarregar os dados da última sessão - isso requer uma abordagem de armazenamento persistente, como o SQLite.
  2. O usuário alterna o aplicativo e volta ao original e deseja continuar de onde parou - salve e restaure os dados do pacote (como dados do estado do aplicativo) onSaveInstanceState()e onRestoreInstanceState()geralmente é adequado.

Se você salvar os dados do estado de maneira persistente, eles poderão ser recarregados em uma onResume()ou onCreate()(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 um InstanceState, 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.

David
fonte
70

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:

import java.util.Date;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class dataHelper {

    private static final String DATABASE_NAME = "autoMate.db";
    private static final int DATABASE_VERSION = 1;

    private Context context;
    private SQLiteDatabase db;
    private OpenHelper oh ;

    public dataHelper(Context context) {
        this.context = context;
        this.oh = new OpenHelper(this.context);
        this.db = oh.getWritableDatabase();
    }

    public void close() {
        db.close();
        oh.close();
        db = null;
        oh = null;
        SQLiteDatabase.releaseMemory();
    }


    public void setCode(String codeName, Object codeValue, String codeDataType) {
        Cursor codeRow = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        String cv = "" ;

        if (codeDataType.toLowerCase().trim().equals("long") == true){
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            cv = String.valueOf(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            cv = String.valueOf(((Date)codeValue).getTime());
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            String.valueOf(codeValue);
        }
        else
        {
            cv = String.valueOf(codeValue);
        }

        if(codeRow.getCount() > 0) //exists-- update
        {
            db.execSQL("update code set codeValue = '" + cv +
                "' where codeName = '" + codeName + "'");
        }
        else // does not exist, insert
        {
            db.execSQL("INSERT INTO code (codeName, codeValue, codeDataType) VALUES(" +
                    "'" + codeName + "'," +
                    "'" + cv + "'," +
                    "'" + codeDataType + "')" );
        }
    }

    public Object getCode(String codeName, Object defaultValue){

        //Check to see if it already exists
        String codeValue = "";
        String codeDataType = "";
        boolean found = false;
        Cursor codeRow  = db.rawQuery("SELECT * FROM code WHERE codeName = '"+  codeName + "'", null);
        if (codeRow.moveToFirst())
        {
            codeValue = codeRow.getString(codeRow.getColumnIndex("codeValue"));
            codeDataType = codeRow.getString(codeRow.getColumnIndex("codeDataType"));
            found = true;
        }

        if (found == false)
        {
            return defaultValue;
        }
        else if (codeDataType.toLowerCase().trim().equals("long") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (long)0;
            }
            return Long.parseLong(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("int") == true)
        {
            if (codeValue.equals("") == true)
            {
                return (int)0;
            }
            return Integer.parseInt(codeValue);
        }
        else if (codeDataType.toLowerCase().trim().equals("date") == true)
        {
            if (codeValue.equals("") == true)
            {
                return null;
            }
            return new Date(Long.parseLong(codeValue));
        }
        else if (codeDataType.toLowerCase().trim().equals("boolean") == true)
        {
            if (codeValue.equals("") == true)
            {
                return false;
            }
            return Boolean.parseBoolean(codeValue);
        }
        else
        {
            return (String)codeValue;
        }
    }


    private static class OpenHelper extends SQLiteOpenHelper {

        OpenHelper(Context context) {
            super(context, DATABASE_NAME, null, DATABASE_VERSION);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL("CREATE TABLE IF  NOT EXISTS code" +
            "(id INTEGER PRIMARY KEY, codeName TEXT, codeValue TEXT, codeDataType TEXT)");
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        }
    }
}

Uma simples ligação depois disso

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;
Mike A.
fonte
9
Como leva muito tempo para carregar um banco de dados SQLite, considerando que esse é o caminho crítico para mostrar ao usuário a interface do usuário do aplicativo. Na verdade, eu não cronometrei, por isso estou feliz em ser corrigido, mas certamente carregar e abrir um arquivo de banco de dados não será rápido?
26412 Tom
5
Muito obrigado por fornecer uma solução que um novato pode cortar e colar no aplicativo e usar imediatamente! @ Tom Quanto à velocidade, leva cerca de sete segundos para armazenar 1000 pares, mas você pode fazê-lo em uma AsyncTask. No entanto, você precisa adicionar finalmente {cursor.close ()}, ou ele trava com o vazamento de memória ao fazer isso.
Noumenon
3
Me deparei com isso e, embora pareça legal, hesito em tentar utilizá-lo no Google Glass, que é o dispositivo em que estou trabalhando / com os últimos tempos.
Stephen Tetreault
61

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:

<activity android:name=".activity2"
          android:alwaysRetainTaskState="true"      
          android:launchMode="singleInstance">
</activity>

E na atividade1 no botão, clique no evento que eu fiz assim:

Intent intent = new Intent();
intent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
intent.setClassName(this,"com.mainscreen.activity2");
startActivity(intent);

E na atividade2 no evento de clique no botão, eu fiz assim:

Intent intent=new Intent();
intent.setClassName(this,"com.mainscreen.activity1");
startActivity(intent);

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.

Roy Matthew
fonte
2
@bagusflyer cuidado para ser mais específico ??? Seu comentário não é útil e ninguém pode ajudá-lo com base nisso.
Stephen Tetreault
2
Esta é uma resposta para uma situação diferente: duas atividades no mesmo aplicativo. OP é sair do aplicativo (por exemplo, botão de início ou outro meio de mudar para um aplicativo diferente).
Home
44

onSaveInstanceState()para dados transitórios (restaurados em onCreate()/ onRestoreInstanceState()), onPause()para dados persistentes (restaurados em onResume()). Dos recursos técnicos do Android:

onSaveInstanceState () é chamado pelo Android se a Atividade estiver sendo interrompida e pode ser eliminada antes de ser retomada! Isso significa que ele deve armazenar qualquer estado necessário para reinicializar na mesma condição quando a Atividade for reiniciada. É a contrapartida do método onCreate () e, de fato, o bundle SavedInstanceState passado para onCreate () é o mesmo Bundle que você constrói como outState no método onSaveInstanceState ().

onPause () e onResume () também são métodos complementares. onPause () é sempre chamado quando a Atividade termina, mesmo que instigemos isso (com uma chamada finish () por exemplo). Usaremos isso para salvar a nota atual de volta no banco de dados. A boa prática é liberar todos os recursos que podem ser liberados durante uma onPause (), para ocupar menos recursos quando no estado passivo.

Ixx
fonte
40

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

u-foka
fonte
37

Para ajudar a reduzir o boilerplate, use o seguinte interfacee classpara ler / gravar em um Bundlepara salvar o estado da instância.


Primeiro, crie uma interface que será usada para anotar suas variáveis ​​de instância:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({
        ElementType.FIELD
})
public @interface SaveInstance {

}

Em seguida, crie uma classe em que a reflexão será usada para salvar valores no pacote configurável:

import android.app.Activity;
import android.app.Fragment;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;

import java.io.Serializable;
import java.lang.reflect.Field;

/**
 * Save and load fields to/from a {@link Bundle}. All fields should be annotated with {@link
 * SaveInstance}.</p>
 */
public class Icicle {

    private static final String TAG = "Icicle";

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #load(Bundle, Object)
     */
    public static void save(Bundle outState, Object classInstance) {
        save(outState, classInstance, classInstance.getClass());
    }

    /**
     * Find all fields with the {@link SaveInstance} annotation and add them to the {@link Bundle}.
     *
     * @param outState
     *         The bundle from {@link Activity#onSaveInstanceState(Bundle)} or {@link
     *         Fragment#onSaveInstanceState(Bundle)}
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #load(Bundle, Object, Class)
     */
    public static void save(Bundle outState, Object classInstance, Class<?> baseClass) {
        if (outState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    field.setAccessible(true);
                    String key = className + "#" + field.getName();
                    try {
                        Object value = field.get(classInstance);
                        if (value instanceof Parcelable) {
                            outState.putParcelable(key, (Parcelable) value);
                        } else if (value instanceof Serializable) {
                            outState.putSerializable(key, (Serializable) value);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not added to the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @see #save(Bundle, Object)
     */
    public static void load(Bundle savedInstanceState, Object classInstance) {
        load(savedInstanceState, classInstance, classInstance.getClass());
    }

    /**
     * Load all saved fields that have the {@link SaveInstance} annotation.
     *
     * @param savedInstanceState
     *         The saved-instance {@link Bundle} from an {@link Activity} or {@link Fragment}.
     * @param classInstance
     *         The object to access the fields which have the {@link SaveInstance} annotation.
     * @param baseClass
     *         Base class, used to get all superclasses of the instance.
     * @see #save(Bundle, Object, Class)
     */
    public static void load(Bundle savedInstanceState, Object classInstance, Class<?> baseClass) {
        if (savedInstanceState == null) {
            return;
        }
        Class<?> clazz = classInstance.getClass();
        while (baseClass.isAssignableFrom(clazz)) {
            String className = clazz.getName();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(SaveInstance.class)) {
                    String key = className + "#" + field.getName();
                    field.setAccessible(true);
                    try {
                        Object fieldVal = savedInstanceState.get(key);
                        if (fieldVal != null) {
                            field.set(classInstance, fieldVal);
                        }
                    } catch (Throwable t) {
                        Log.d(TAG, "The field '" + key + "' was not retrieved from the bundle");
                    }
                }
            }
            clazz = clazz.getSuperclass();
        }
    }

}

Exemplo de uso:

public class MainActivity extends Activity {

    @SaveInstance
    private String foo;

    @SaveInstance
    private int bar;

    @SaveInstance
    private Intent baz;

    @SaveInstance
    private boolean qux;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Icicle.load(savedInstanceState, this);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Icicle.save(outState, this);
    }

}

Nota: Este código foi adaptado de um projeto de biblioteca chamado AndroidAutowire, licenciado sob a licença MIT .

Jared Rummler
fonte
34

Enquanto isso, eu geralmente não uso mais

Bundle savedInstanceState & Co

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:

 SharedPreferences p;
 p.edit().put(..).commit()

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.

stefan bachert
fonte
31

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:

  • Alterações na configuração, como alterar a orientação ou o idioma do telefone, o que pode exigir a criação de uma nova instância de atividade.
  • Você volta ao aplicativo em segundo plano depois que o sistema operacional destrói a atividade.

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.

  • Quando você pressiona o botão Voltar, a Atividade está concluída. O relançamento do aplicativo é uma instância totalmente nova. Você não está retomando a fundo.
  • Quando você pressiona o botão home ou usa o alternador de tarefas, a Atividade entra em segundo plano. Ao navegar de volta para o aplicativo, o onCreate será chamado apenas se a Atividade tiver que ser destruída.

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.

Jared Kells
fonte
28

Os métodos onSaveInstanceState(bundle)e onRestoreInstanceState(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, mas onCreate(bundle)e onRestoreInstanceState(bundle)não é invocado novamente.
Para mais preferências uso compartilhado persistência. Ler este artigo

Mahorad
fonte
2
No seu caso, onCreatee onRestoreInstanceStatenão está sendo chamado porque ele Activitynão é destruído quando você alterna entre aplicativos, portanto, não há necessidade de restaurar nada. O Android chama onSaveInstanceStateapenas 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).
Vicky Chijwani
20

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:

mySavedInstanceState=savedInstanceState;

e use isso para obter o conteúdo da minha variável quando eu precisar, nas linhas de:

if (mySavedInstanceState !=null) {
   boolean myVariable = mySavedInstanceState.getBoolean("MyVariable");
}

Eu uso onSaveInstanceStatee onRestoreInstanceStatecomo 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, usando putBoolean)

torwalker
fonte
19

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:

class MainActivity extends Activity {
  @State String username; // These will be automatically saved and restored
  @State String password;
  @State int age;

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

É o mesmo que fazer isso:

class MainActivity extends Activity {
  String username;
  String password;
  int age;

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    savedInstanceState.putString("MyString", username);
    savedInstanceState.putString("MyPassword", password);
    savedInstanceState.putInt("MyAge", age); 
    /* remember you would need to actually initialize these variables before putting it in the
    Bundle */
  }

  @Override
  public void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    username = savedInstanceState.getString("MyString");
    password = savedInstanceState.getString("MyPassword");
    age = savedInstanceState.getInt("MyAge");
  }
}

O Icepick funcionará com qualquer objeto que salvar seu estado com a Bundle.

kevinc
fonte
16

Quando uma atividade é criada, o método onCreate () é chamado.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

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

   @Override
    protected void onSaveInstanceState(Bundle outState) {
      outState.putString("key","Welcome Back")
        super.onSaveInstanceState(outState);       //save state
    }

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.

   @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

          //restore activity's state
         if(savedInstanceState!=null){
          String reStoredString=savedInstanceState.getString("key");
            }
    }

ou

  //restores activity's saved state
 @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
      String restoredMessage=savedInstanceState.getString("key");
    }
Mansuu ....
fonte
15

Existem basicamente duas maneiras de implementar essa mudança.

  1. usando onSaveInstanceState()e onRestoreInstanceState().
  2. Em manifesto 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.

class MyModel extends Serializable{
JSONObject obj;

setJsonObject(JsonObject obj)
{
this.obj=obj;
}

JSONObject getJsonObject()
return this.obj;
} 
}

Agora, na sua atividade nos métodos onCreate e onSaveInstanceState, faça o seguinte. Vai parecer algo assim:

@override
onCreate(Bundle savedInstaceState){
MyModel data= (MyModel)savedInstaceState.getSerializable("yourkey")
JSONObject obj=data.getJsonObject();
//Here you have retained JSONObject and can use.
}


@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
//Obj is some json object 
MyModel dataToSave= new MyModel();
dataToSave.setJsonObject(obj);
oustate.putSerializable("yourkey",dataToSave); 

}
Krishna
fonte
11

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)

@VVK - Eu discordo parcialmente. Algumas maneiras de sair de um aplicativo não são acionadas no SaveInstanceState (oSIS). Isso limita a utilidade do oSIS. Vale a pena oferecer suporte, para recursos mínimos do sistema operacional, mas se um aplicativo quiser retornar o usuário ao estado em que estava, não importa como o aplicativo foi encerrado, é necessário usar uma abordagem de armazenamento persistente. Uso o onCreate para verificar o pacote e, se estiver ausente, verifique o armazenamento persistente. Isso centraliza a tomada de decisão. Posso me recuperar de uma falha ou sair do botão voltar ou do item de menu personalizado Sair ou voltar ao usuário da tela muitos dias depois. Você está em: Home 'Ferramentas' Ferramentas '

Sam é
fonte
10

Código Kotlin:

Salve :

override fun onSaveInstanceState(outState: Bundle) {
    super.onSaveInstanceState(outState.apply {
        putInt("intKey", 1)
        putString("stringKey", "String Value")
        putParcelable("parcelableKey", parcelableObject)
    })
}

e depois em onCreate()ouonRestoreInstanceState()

    val restoredInt = savedInstanceState?.getInt("intKey") ?: 1 //default int
    val restoredString = savedInstanceState?.getString("stringKey") ?: "default string"
    val restoredParcelable = savedInstanceState?.getParcelable<ParcelableClass>("parcelableKey") ?: ParcelableClass() //default parcelable

Adicione valores padrão se você não quiser ter opcionais

Rafols
fonte
9

Para obter os dados do estado da atividade armazenados onCreate(), primeiro você deve salvar os dados em savedInstanceState pelo SaveInstanceState(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 mesmo onCreate()quando a atividade é reiniciada. (SavedInstanceState não será nulo, pois você salvou alguns dados antes da atividade ser destruída)

ascii_walker
fonte
6

Simples e rápido para resolver este problema está usando o IcePick

Primeiro, configure a biblioteca em app/build.gradle

repositories {
  maven {url "https://clojars.org/repo/"}
}
dependencies {
  compile 'frankiesardo:icepick:3.2.0'
  provided 'frankiesardo:icepick-processor:3.2.0'
}

Agora, vamos verificar este exemplo abaixo como salvar o estado em Activity

public class ExampleActivity extends Activity {
  @State String username; // This will be automatically saved and restored

  @Override public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Icepick.restoreInstanceState(this, savedInstanceState);
  }

  @Override public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Icepick.saveInstanceState(this, outState);
  }
}

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:

class CustomView extends View {
  @State int selectedPosition; // This will be automatically saved and restored

  @Override public Parcelable onSaveInstanceState() {
    return Icepick.saveInstanceState(this, super.onSaveInstanceState());
  }

  @Override public void onRestoreInstanceState(Parcelable state) {
    super.onRestoreInstanceState(Icepick.restoreInstanceState(this, state));
  }

  // You can put the calls to Icepick into a BaseCustomView and inherit from it
  // All Views extending this CustomView automatically have state saved/restored
}
THANN Phearum
fonte
1
@ralphspoon sim, funciona para Fragment e Custom View. Por favor, verifique o código de exemplo. Eu editei minha resposta. Sugiro que você vá para documentos oficiais aqui github.com/frankiesardo/icepick para encontrar mais exemplos de código.
THANN Phearum 14/09/16
@ChetanMehra, você quer dizer classe de exibição personalizada, certo? Se for uma visualização personalizada, podemos substituir onSaveInstanceState e onRestoreInstanceState como no exemplo acima do CustomView.
THANN Phearum 17/05/19
Quero dizer objeto de classe vista privilegiada classe por exemplo: class CustomView estende Ver {@State ClassA a;} ou classe CustomView estende Ver {@ classe Estado Inner {}}
Chetan Mehra
@THANNPhearum Devo fazer isso como outra pergunta?
Chetan Mehra
Eu vejo. Nesse caso, seu ClassA deve ser Parcelable. Como se mencionou que ele funciona para Atividades, fragmentos ou qualquer objeto que precisa para serializar seu estado em um Bundle
THANN Phearum
6

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.

Entre
fonte
5

Kotlin

Você deve substituir onSaveInstanceStatee onRestoreInstanceStatearmazenar e recuperar suas variáveis ​​que deseja que sejam persistentes

Gráfico do ciclo de vida

Armazenar variáveis

public override fun onSaveInstanceState(savedInstanceState: Bundle) {
    super.onSaveInstanceState(savedInstanceState)

    // prepare variables here
    savedInstanceState.putInt("kInt", 10)
    savedInstanceState.putBoolean("kBool", true)
    savedInstanceState.putDouble("kDouble", 4.5)
    savedInstanceState.putString("kString", "Hello Kotlin")
}

Recuperar variáveis

public override fun onRestoreInstanceState(savedInstanceState: Bundle) {
    super.onRestoreInstanceState(savedInstanceState)

    val myInt = savedInstanceState.getInt("kInt")
    val myBoolean = savedInstanceState.getBoolean("kBool")
    val myDouble = savedInstanceState.getDouble("kDouble")
    val myString = savedInstanceState.getString("kString")
    // use variables here
}
Sazzad Hissain Khan
fonte
2

Agora, o Android fornece ViewModels para salvar o estado, você deve tentar usá-lo em vez de saveInstanceState.

M Abdul Sami
fonte
3
Isso não é verdade. Na documentação: "Ao contrário do estado da instância salva, os ViewModels são destruídos durante uma morte no processo iniciada pelo sistema. É por isso que você deve usar os objetos ViewModel em combinação com onSaveInstanceState () (ou alguma outra persistência do disco), escondendo identificadores em savedInstanceState para ajudar a exibir modelos recarregam os dados após a morte do sistema ".
Vyacheslav Martynenko
Acabei de encontrar isso com as permissões alteradas em segundo plano.
Brill Pappin
Concordo que, no documento "se você precisar lidar com a morte do processo iniciado pelo sistema, poderá usar onSaveInstanceState () como backup".
Zhar
2

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:

android:configChanges="orientation|screenSize"

Deve ficar assim:

<activity
    android:name=".activities.MyActivity"
    android:configChanges="orientation|screenSize">
</activity>

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.

IgniteCoders
fonte
2
Isso não tem nada a ver com salvar o estado, você apenas renuncia a alterações de orientação, lembre-se de que o aplicativo pode ser reiniciado, pausado e retomado a qualquer momento para diferentes eventos
lord-ralf-adolf
1
Esta resposta é para aqueles que querem salvar o estado quando a orientação mudou e querem evitar a compreensão e implementação de uma maneira complexa
IgniteCoders
justo o suficiente, entendo seu ponto de vista, acho que a maioria das pessoas que luta para salvar o estado está usando fragmentos porque as atividades realmente salvam o status dos componentes da interface do usuário desde que tenham um ID, mas os fragmentos são mais especiais, usei fragmentos uma vez, mas nunca usarei eles novamente, o stat da instância de save foi
difícil de
funciona ... obrigado
Fanadez
1

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

Por padrão, o sistema salva os objetos View no Bundle, por exemplo.

  • Texto em EditText
  • Role a posição em a 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 currentScoreem um GameActivity

Mais detalhes sobre o onSavedInstanceState (Bundle savedinstaneState) ao salvar dados

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    // Save the user's current game state
    savedInstanceState.putInt(STATE_SCORE, mCurrentScore);

    // Always call the superclass so it can save the view hierarchy state
    super.onSaveInstanceState(savedInstanceState);
}

Portanto, por engano, se você esquecer de chamar super.onSaveInstanceState(savedInstanceState);o comportamento padrão, não funcionará, ou seja, o texto no EditText não será salvo.

Qual escolher para restaurar o estado da atividade?

 onCreate(Bundle savedInstanceState)

OU

onRestoreInstanceState(Bundle savedInstanceState)

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)

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);

    // Restore state members from the saved instance
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
}

Sempre chame super.onRestoreInstanceState(savedInstanceState);para que o sistema restaure a hierarquia Exibir por padrão

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 .

Rohit Singh
fonte
1

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.

public class HelloAndroidViewModel extends ViewModel {
   public Booelan firstInit = false;

    public HelloAndroidViewModel() {
        firstInit = false;
    }
    ...
}

public class HelloAndroid extends Activity {

  private TextView mTextView = null;
  HelloAndroidViewModel viewModel = ViewModelProviders.of(this).get(HelloAndroidViewModel.class);
  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    mTextView = new TextView(this);

    //Because even if the state is deleted, the data in the viewmodel will be kept because the activity does not destroy
    if(!viewModel.firstInit){
        viewModel.firstInit = true
        mTextView.setText("Welcome to HelloAndroid!");
    }else{
       mTextView.setText("Welcome back.");
    }

    setContentView(mTextView);
  }
}
Umut ADALI
fonte
você está certo, mas esta biblioteca ainda está em versão então eu acho que devemos esperar ...
Zhar