savedinstancestate Salvando o estado de atividade do Android usando Salvar estado da instância




savedinstancestate android (22)

Observe que não é seguro usar onSaveInstanceState e onRestoreInstanceState para dados persistentes , de acordo com a documentação sobre estados de atividade em developer.android.com/reference/android/app/Activity.html .

O documento indica (na seção 'Activity Lifecycle'):

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

Em outras palavras, coloque seu código de salvamento / restauração para dados persistentes em onPause() e onResume() !

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

Esse método é chamado antes que uma atividade possa ser eliminada, de modo que, quando voltar algum tempo no futuro, possa restaurar seu estado. Por exemplo, se a atividade B for ativada em frente à atividade A e em algum momento a atividade A for eliminada para recuperar recursos, a atividade A terá a chance de salvar o estado atual de sua interface do usuário por esse método, 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) .

Eu tenho trabalhado na plataforma Android SDK, e é um pouco claro como salvar o estado de um aplicativo. Então, dada essa pequena ferramenta do exemplo "Hello, 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 suficiente para o caso mais simples, mas sempre responde com a primeira mensagem, não importa como eu ando longe do aplicativo.

Tenho certeza de que a solução é tão simples quanto substituir o onPause ou algo assim, mas eu tenho onPause na documentação por 30 minutos ou mais e não encontrei nada óbvio.


Agora o Android fornece ViewModels para salvar o estado, você deve tentar usar isso em vez de saveInstanceState.


Meu colega escreveu um artigo explicando o estado do aplicativo em dispositivos Android, incluindo explicações sobre o ciclo de vida da atividade e informações de estado, como armazenar informações de estado e salvando em Bundle estado e SharedPreferences e dê uma olhada aqui .

O artigo abrange três abordagens:

Armazenar dados de controle de variáveis ​​/ interface do usuário locais para o tempo de vida do aplicativo (ou seja, temporariamente) usando o Pacote de estado de 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);
}

Armazenar dados de controle de variáveis ​​/ interface do usuário locais entre instâncias do aplicativo (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();
}

Mantendo as instâncias de objeto ativas na memória entre atividades dentro do tempo de vida do aplicativo usando Instância de configuração nã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();
}

Simples rápido para resolver esse problema é 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 neste 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 Bundle (por exemplo, ViewPresenters de morteiros)

O Icepick também pode gerar o código do estado da instância para exibiçõ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
}

É necessário substituir onSaveInstanceState(Bundle savedInstanceState) e gravar os valores de estado do aplicativo que você deseja alterar para o parâmetro Bundle seguinte 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 maneira de armazenar um mapa NVP ("Pair de Nome-Valor"), e ele será passado para onCreate() e também onRestoreInstanceState() onde você onRestoreInstanceState() os valores da seguinte forma:

@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");
}

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


Meu problema era que eu precisava de persistência apenas durante a vida útil da aplicação (ou seja, uma única execução, incluindo iniciar outras subatividades dentro do mesmo aplicativo e girar o dispositivo, etc.). Eu 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 o onCreate:

mySavedInstanceState=savedInstanceState;

e usar isso para obter o conteúdo da minha variável quando eu precisasse, ao longo das linhas de:

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

Eu uso onSaveInstanceStatee onRestoreInstanceStatecomo sugerido acima, mas eu acho que eu poderia também ou, alternativamente, usar o meu método para salvar a variável quando ela muda (por exemplo, usando putBoolean)


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

onSaveInstanceState () é chamado pelo Android se a atividade está sendo interrompida e pode ser eliminada antes de ser retomada! Isso significa que deve armazenar qualquer estado necessário para reinicializar na mesma condição quando a atividade for reiniciada. É a contrapartida do método onCreate () e, na verdade, 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 Activity termina, mesmo que tenhamos instigado 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 um onPause (), para ocupar menos recursos quando no estado passivo.


O estado de poupança é um kludge na melhor das hipóteses, tanto quanto eu estou preocupado. Se você precisar salvar dados persistentes, basta usar um SQLite dados SQLite . O Android torna o SOOO 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;

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 . O Icepick é um processador de anotações que cuida de todo o código padrão utilizado para salvar e restaurar o estado para você.

Fazendo algo assim com o 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 salve seu estado com um Bundle.


Existem basicamente duas maneiras de implementar essa mudança.

  1. usando onSaveInstanceState()e onRestoreInstanceState().
  2. No manifesto android:configChanges="orientation|screenSize".

Eu realmente não recomendo usar o segundo método. Como em uma das minhas experiências, estava causando metade da tela do dispositivo preta ao girar de retrato para paisagem e vice-versa.

Usando o primeiro método mencionado acima, podemos persistir dados quando a orientação é alterada ou qualquer alteração de configuração acontece. Eu sei uma maneira em que você pode armazenar qualquer tipo de dados dentro do objeto de estado savedInstance.

Exemplo: Considere um caso se você quiser 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, em sua atividade no método 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); 

}

Eu acho que encontrei a resposta. Deixe-me dizer o que eu fiz em palavras simples:

Suponha que eu tenha duas atividades, activity1 e activity2 e eu esteja navegando de activity1 para activity2 (já fiz alguns trabalhos em activity2) e novamente para a atividade 1, clicando em um botão em activity1. Agora, neste estágio, eu queria voltar para a atividade2 e quero ver minha atividade2 na mesma condição quando saí 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 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 botão clique em evento eu fiz assim:

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

Agora, o que acontecerá é que, sejam quais forem as mudanças que tenhamos feito na atividade2, não serão perdidas, e poderemos ver a atividade2 no mesmo estado que deixamos anteriormente.

Eu acredito que esta é a resposta e isso funciona bem para mim. Corrija-me se eu estiver enganado.


Para ajudar a reduzir o clichê, uso o seguinte interfacee classpara ler / gravar em um Bundleestado de instância para salvar.

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:

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 que é licenciado sob a licença MIT .


Para responder a pergunta original diretamente. savedInstancestate é null porque sua atividade nunca está sendo recriada.

Sua atividade só será recriada com um pacote de estado quando:

  • Alterações na configuração, como alterar a orientação ou a linguagem do telefone, o que pode exigir que uma nova instância de atividade seja criada.
  • Você retorna ao aplicativo do segundo plano depois que o sistema operacional destruiu a atividade.

O Android destruirá as atividades em segundo plano quando estiver sob pressão de memória ou depois de ficar em segundo plano por um longo período de tempo.

Ao testar seu exemplo de hello world, há algumas maneiras de sair e retornar à Activity.

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

Na maioria dos casos, se você pressionar a página inicial e ativar o aplicativo novamente, a atividade não precisará ser recriada. Já existe na memória, então onCreate () não será chamado.

Existe uma opção em Configurações -> Opções do desenvolvedor chamada "Não manter atividades". Quando ativado, o Android sempre destruirá as atividades e as recriará quando estiverem em segundo plano. Essa é uma ótima opção para deixar ativada durante o desenvolvimento porque simula o pior cenário possível. (Um dispositivo de baixa memória reciclar suas atividades o tempo todo).

As outras respostas são valiosas, pois ensinam as maneiras corretas de armazenar o estado, mas não senti que elas realmente respondiam PORQUE seu código não estava funcionando da maneira esperada.


Enquanto isso eu faço em geral não mais uso

Bundle savedInstanceState & Co

o ciclo ao vivo é para a maioria das atividades muito complicado e desnecessário. E google afirma-se, não é mesmo confiável.

Meu jeito é salvar as alterações imediatamente nas preferências

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

de alguma forma, o SharedPreferences funciona de maneira similar ao Bundles. E naturalmente e a princípio tais valores têm que ser vermelhos das preferências.

No caso de dados complexos, você pode usar o 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 no meio ou uma reabertura devido à pilha de trás.


Aqui está um comentário da resposta de Steve Moseley (por ToolmakerSteve ) que coloca as coisas em perspectiva (em todo onSaveInstanceState vs onPause, custo leste vs saga custo oeste)

@VVK - Eu discordo parcialmente. Algumas formas de sair de um aplicativo não acionam onSaveInstanceState (oSIS). Isso limita a utilidade do oSIS. Vale a pena dar suporte a 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. Eu uso onCreate para verificar o bundle e, se estiver faltando, verifique o armazenamento persistente. Isso centraliza a tomada de decisão. Eu posso recuperar de um acidente, ou sair do botão de volta ou item de menu personalizado Sair, ou voltar para o usuário tela estava em muitos dias depois. - ToolmakerSteve 19 de setembro às 15h38


Adicionando o LiveData (componentes de arquitetura do Android) ao seu projeto

adicione a seguinte dependência

implementation "android.arch.lifecycle:extensions:1.1.0"

O LiveData recebe um observador e o notifica sobre alterações de dados somente quando está no estado INICIADO ou RESUMIDO. A vantagem com o LiveData é que quando sua atividade entra em qualquer estado diferente de STARTED ou RESUMED, ele não chamará o método onChanged no observador .

private TextView mTextView;
private MutableLiveData<String> mMutableLiveData;

@Override
protected void onCreate(Bundle savedInstanceState) {
    mTextView = (TextView) findViewById(R.id.textView);
    mMutableLiveData = new MutableLiveData<>();
    mMutableLiveData.observe(this, new Observer<String>() {
        @Override
        public void onChanged(@Nullable String s) {
            mTextView.setText(s);
        }
    });

}

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 reabre-o em uma data posterior, mas o aplicativo precisa recarregar os dados da última sessão - isso requer uma abordagem de armazenamento persistente, como o uso do SQLite.
  2. O usuário alterna o aplicativo, retorna ao original e quer continuar de onde parou - salvar e restaurar dados do pacote (como dados do estado do aplicativo) em onSaveInstanceState() e onRestoreInstanceState() geralmente é adequado.

Se você salvar os dados de estado de maneira persistente, eles poderão ser recarregados em um onResume() ou onCreate() (ou, na verdade, em qualquer chamada de ciclo de vida). Isso pode ou não ser um comportamento desejado. Se você armazená-lo em um pacote em um InstanceState , ele será transitório e só será adequado para armazenar dados para uso na mesma sessão de usuário (uso o termo sessão de forma imprecisa), mas não entre 'sessões'.

Não é que uma abordagem seja melhor que a outra, como tudo, é importante entender o comportamento de que você precisa e selecionar a abordagem mais apropriada.


O savedInstanceState é apenas para salvar o estado associado a uma instância atual de uma Activity, por exemplo, informações atuais de navegação ou seleção, para que, se o Android destruir e recriar uma Activity, possa voltar como era antes. Veja a documentação para onCreate e onSaveInstanceState

Para um estado mais longo, considere usar um banco de dados SQLite, um arquivo ou preferências. Veja Salvando Estado Persistente .


Recriando uma atividade

Existem alguns cenários em que sua atividade é destruída devido ao comportamento normal do aplicativo, como quando o usuário pressiona o botão Voltar ou sua atividade sinaliza sua própria destruição chamando finish() . O sistema também pode destruir sua atividade se estiver parada no momento e não for usada há muito tempo ou se a atividade em primeiro plano exigir mais recursos, portanto, o sistema deve desligar os processos em segundo plano para recuperar a memória.

Quando sua activity é destruída porque o usuário pressiona Voltar ou a activity concluída, o conceito do sistema dessa instância de Activity desaparece para sempre porque o comportamento indica que a atividade não é mais necessária. No entanto, se o sistema destrói a atividade devido a restrições do sistema (em vez do comportamento normal do aplicativo), embora a instância real de Atividade tenha desaparecido, o sistema lembra que existia de tal forma que, se o usuário navegar de volta para ela, o sistema criará uma nova. instância da atividade usando um conjunto de dados salvos que descreve o estado da atividade quando ela foi destroyed . Os dados salvos que o sistema usa para restaurar o estado anterior são chamados de "estado da instância" e são uma coleção de pares de valores-chave armazenados em um objeto Bundle.

Para salvar dados adicionais sobre o estado da atividade, você deve substituir o método de retorno de chamada onSaveInstanceState (). O sistema chama esse método quando o usuário está deixando sua atividade e passa o objeto Bundle que será salvo no caso de sua atividade ser destruída inesperadamente. Se o sistema precisar recriar a instância de atividade posteriormente, ele passará o mesmo objeto Bundle para os onRestoreInstanceState() e onCreate() .

À medida que o sistema começa a interromper sua atividade, ele chama onSaveInstanceState() (1) para que você possa especificar dados de estado adicionais que gostaria de salvar caso a instância Activity seja recriada. Se a atividade é destruída e a mesma instância deve ser recriada, o sistema passa os dados de estado definidos em (1) para o método onCreate() (2) e o método onRestoreInstanceState() (3).

Salvar seu estado de Activity

À medida que sua atividade começa a parar, o sistema chama onSaveInstanceState() para que sua atividade possa salvar informações de estado com uma coleção de pares de valores-chave. A implementação padrão desse método salva informações sobre o estado da hierarquia de EditText da atividade, como o texto em um widget EditText ou a posição de rolagem de um ListView .

Para salvar informações de estado adicionais para sua atividade, você deve implementar onSaveInstanceState() e adicionar pares de valores-chave ao objeto Bundle. Por exemplo:

  static final String STATE_SCORE = "playerScore";
  static final String STATE_LEVEL = "playerLevel";

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

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

Cuidado: Sempre chame a implementação da superclasse de onSaveInstanceState() para que a implementação padrão possa salvar o estado da hierarquia de exibição.

Restaurar seu estado de Activity

Quando sua atividade é recriada após ter sido destruída anteriormente, você pode recuperar seu estado salvo do Bundle pelo qual o sistema passa sua atividade. Os métodos de retorno de chamada onCreate() e onRestoreInstanceState() recebem o mesmo Bundle que contém as informações de estado da instância.

Como o método onCreate() é chamado se o sistema está criando uma nova instância de sua atividade ou recriando uma anterior, você deve verificar se o estado Bundle é nulo antes de tentar lê-lo. Se for nulo, o sistema está criando uma nova instância da atividade, em vez de restaurar uma anterior que foi destruída.

Por exemplo, aqui está como você pode restaurar alguns dados de estado em onCreate() :

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); // Always call the superclass first

 // Check whether we're recreating a previously destroyed instance
 if (savedInstanceState != null) {
    // Restore value of members from saved state
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
 } else {
    // Probably initialize members with default values for a new instance
 }

 }

Em vez de restaurar o estado durante onCreate() você pode escolher implementar onRestoreInstanceState() , que o sistema chama depois do método onStart() . O sistema chama onRestoreInstanceState() somente se houver um estado salvo para restauração, portanto, você não precisa verificar se o Bundle é nulo:

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

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

Os métodos onSaveInstanceState(bundle)e onRestoreInstanceState(bundle)são úteis para persistência de 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


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

Adicionar valores padrão se você não quiser ter opções


Para obter os dados do estado da atividade armazenados onCreate(), primeiro salve os dados no SaveInstanceState(Bundle savedInstanceState)método savedInstanceState substituindo .

Quando o SaveInstanceState(Bundle savedInstanceState)método de destruição de atividade é chamado, você salva os dados que deseja salvar. E você recebe o mesmo onCreate()quando a atividade é reiniciada. (SavedInstanceState não será nulo porque você salvou alguns dados antes que a atividade seja destruída)





application-state