Pergunta

Eu tenho trabalhado na plataforma SDK Android, e é um pouco claro como salvar o estado de um aplicativo. Assim, dada esta minor re-ferramentas do 'Olá, Android' exemplo:

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 navegar para fora da aplicação.

Eu tenho certeza que a solução é tão simples como substituir onPause ou algo assim, mas eu fui cutucando fora na documentação para 30 minutos ou mais e não encontrei nada óbvio.

Foi útil?

Solução

Você precisa substituir onSaveInstanceState(Bundle savedInstanceState) e escrever os valores de estado de aplicações que pretende mudar para o parâmetro Bundle assim:

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

Você normalmente usar esta técnica para valores de instância de loja para o seu aplicativo (seleções, texto salvos, etc.).

Outras dicas

O savedInstanceState é apenas para salvar o estado associada a uma instância atual de uma atividade, por exemplo, a navegação atual ou informações de seleção, de modo que se destrói Android e recria uma atividade, ele pode voltar como era antes. Consulte a documentação do onCreate onSaveInstanceState

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

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

O documento afirma (na seção 'Atividade Lifecycle'):

Note que é importante para salvar dados persistentes em onPause() vez de onSaveInstanceState(Bundle) porque o posterior não faz parte do ciclo de vida chamadas de retorno, de modo que não será chamado em cada situação descrita em sua documentação.

Em outras palavras, colocar o seu código de save / restore dos dados persistentes no onPause() e onResume()!

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

Este método é chamado antes de uma atividade pode ser morto para que quando ele volta em algum momento no futuro, pode restaurar o seu estado. Para exemplo, se a actividade da B é lançado na frente da actividade de A, e em alguns atividade ponto A é morto a recursos recuperação, a atividade A terá uma chance de salvar o estado atual de sua interface de usuário através deste Método de modo que quando o utilizador retorna para a atividade A, o estado do interface de utilizador pode ser restaurada através onCreate(Bundle) ou onRestoreInstanceState(Bundle).

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 salvar a Bundle Estado e SharedPreferences e dar uma olhada aqui .

O artigo abrange três abordagens:

Loja variável local / dados de controle de UI para vida de aplicações (ou seja temporariamente) usando um estado instância pacote

[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);
}

Loja variável local / dados de controle da interface do usuário entre instâncias do aplicativo (ou seja permanentemente) usando as 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 vivos na memória entre as atividades dentro da vida aplicativo usando um retidos instância não-configuração

[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();
}

Esta é uma 'pegadinha' clássico de desenvolvimento Android. Há duas questões aqui:

  • Há uma sutil bug Framework Android que complica muito a gestão pilha de aplicativos durante o desenvolvimento, pelo menos em versões legadas (não inteiramente certo se / quando / como foi fixado). Eu vou discutir este bug abaixo.
  • A maneira 'normal' ou destinado a gerir esta questão é, em si, em vez complicado com a dualidade de onPause / onResume e onSaveInstanceState / onRestoreInstanceState

Navegação em todos esses tópicos, eu suspeito que grande parte dos desenvolvedores de tempo estão falando sobre estas duas questões diferentes simultaneamente ... daí toda a confusão e relatos de "isso não funciona para mim".

Em primeiro lugar, para esclarecer o comportamento 'destinado': onSaveInstance e onRestoreInstance são frágeis e apenas para estado transitório. O uso pretendido (AFAICT) é lidar com atividades de recreação quando o telefone é girado (mudança de orientação). Em outras palavras, o uso pretendido é quando sua atividade ainda é logicamente 'em cima', mas ainda deve ser reinstantiated pelo sistema. O Bundle salvo não é mantido fora do processo / memória / gc, então você não pode realmente contar com isso se sua atividade vai para o fundo. Sim, talvez a memória do seu Activity vai sobreviver a sua viagem para o fundo e escapar GC, mas isso não é confiável (nem é previsível).

Então, se você tiver um cenário onde não é significativo 'progresso usuário' ou o estado que deve ser mantida entre 'lança' da sua aplicação, a orientação é usar onPause e onResume. Você deve escolher e preparar um armazenamento persistente si mesmo.

Mas - há um bug muito confuso o que complica tudo isso. Os detalhes estão aqui:

http://code.google.com/p/android/ problemas / detalhes? id = 2373

http://code.google.com/p/android/ problemas / detalhes? id = 5277

Basicamente, se sua aplicação é iniciada com a bandeira SingleTask, e, em seguida, mais tarde você lançá-lo na tela inicial ou no menu lançador, em seguida, que a invocação posterior criará uma tarefa NOVO ... você vai efetivamente ter duas instâncias diferentes do seu aplicativo habitando a mesma pilha ... que fica muito estranho muito rápido. Isto parece acontecer quando você iniciar o seu aplicativo durante o desenvolvimento (ou seja, a partir de Eclipse ou IntelliJ), para que os desenvolvedores executar para esse muito. Mas também através de alguns dos mecanismos loja de aplicativos de atualização (por isso afeta seus usuários também).

Eu lutei através destes tópicos para horas antes de eu percebi que a minha questão principal foi este erro, não o comportamento enquadramento pretendido. Um grande writeup e solução alternativa (UPDATE: veja abaixo) parece ser de @kaciula usuário nesta resposta:

Início comportamento pressione a tecla

Atualizar junho 2013 : Meses mais tarde, eu finalmente encontrei a solução 'correta'. Você não precisa de gerir todas as bandeiras startedApp stateful, você pode detectar isso a partir do quadro e socorrer adequadamente. Eu uso este perto do início da minha 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;
    }
}

onSaveInstanceState é chamado quando o sistema precisa de memória e mata um aplicativo. Não é chamado quando o usuário apenas fecha o aplicativo. Então eu acho que o estado do aplicativo também deve ser salvo em onPause Ele deve ser salvo em algum tipo de armazenamento persistente como Preferences ou Sqlite

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

  1. O usuário termina a aplicação e re-abre-o em uma data posterior, mas as necessidades de aplicação aos dados de recarga da última sessão -. Isso requer uma abordagem de armazenamento persistente, tais como a utilização de SQLite
  2. O usuário alterna aplicação e, em seguida, volta para o original e quer pegar onde parou -. Salvar e restaurar dados de pacote (tais como dados de estado do aplicativo) no onSaveInstanceState() e onRestoreInstanceState() é geralmente adequada

Se você salvar os dados de estado de uma forma persistente, que pode ser recarregado em uma onResume() ou onCreate() (ou, na verdade, em qualquer chamada de ciclo de vida). Isto pode ou não pode ser o comportamento desejado. Se você armazená-lo em um pacote em uma InstanceState, então é transitória e só é adequado para o armazenamento de dados para uso no mesmo usuário ‘sessão’ (eu uso a sessão termo vagamente), mas não entre ‘sessões’.

Não é que uma abordagem é melhor que o outro, como tudo, é apenas importante para entender o que o comportamento desejado e selecionar a abordagem mais adequada.

estado Saving é um remendo na melhor das hipóteses, tanto quanto eu estou preocupado. Se você precisa salvar dados persistentes, basta usar um href="http://en.wikipedia.org/wiki/SQLite" rel="noreferrer"> SQLite banco de dados . Android faz com que seja SOOO fácil.

Algo parecido com isto:

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 chamada depois que

dataHelper dh = new dataHelper(getBaseContext());
String status = (String) dh.getCode("appState", "safetyDisabled");
Date serviceStart = (Date) dh.getCode("serviceStartTime", null);
dh.close();
dh = null;

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

Suponha que eu tenho duas atividades, Activity1 e Activity2 e estou navegando a partir Activity1 para Activity2 (Eu tenho feito alguns trabalhos em Activity2) e novamente de volta à atividade 1 clicando em um botão na Activity1. Agora, nesta fase, eu queria voltar para Activity2 e eu quero ver o meu Activity2 na mesma condição quando eu última Activity2 esquerda.

Para o cenário acima o que eu fiz é que no manifesto eu fiz algumas mudanças como este:

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

E no Activity1 sobre o evento clique de botão eu fiz assim:

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

E em Activity2 no evento clique do botão que eu fiz assim:

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

Agora, o que vai acontecer é que quaisquer que sejam as mudanças que fizemos no Activity2 não será perdido, e nós podemos ver Activity2 no mesmo estado que saímos anteriormente.

Eu acredito que esta é a resposta e esta multa funciona para mim. Corrija-me se estou errado.

onSaveInstanceState() para dados transitório (restaurado em onCreate() / onRestoreInstanceState()), onPause() de dados persistentes (restaurado em onResume()). De recursos técnicos Android:

onSaveInstanceState () é chamado por Android se a atividade está sendo interrompido e pode ser morto antes de ser retomado! Isso significa que ele deve armazenar qualquer estado necessário para reinicializar à mesma condição quando a atividade é reiniciado. É a contrapartida do método onCreate (), e de fato o savedInstanceState Bundle passado para onCreate () é o mesmo pacote que você construir como outState na onSaveInstanceState () método.

onPause () e onResume () são também métodos de cortesia. onPause () é sempre chamado quando termina Actividade, mesmo se instigado que (com uma chamada de acabamento () por exemplo). Vamos usar isso para salvar a volta nota atual para o banco de dados. A boa prática é liberar quaisquer recursos que podem ser libertadas durante um onPause (), bem como, para ocupar menos recursos quando no estado passivo.

Realmente estado onSaveInstance Callen quando a Atividade vai para fundo

Citação das docs: "O método onSaveInstanceState(Bundle) é chamado antes de colocar a atividade em tal estado um fundo"

Para ajudar a reduzir clichê eu uso o seguinte interface e class para leitura / gravação a um Bundle para salvar estado da instância.


Em primeiro lugar, criar uma interface que será usado 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 onde a reflexão será usado para salvar valores para o 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 a partir de um projeto de biblioteca chamado AndroidAutowire que está licenciado sob a < a href = "https://raw.githubusercontent.com/CardinalNow/AndroidAutowire/master/LICENSE"> MIT licença .

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

Bundle savedInstanceState & Co

O ciclo de vida é para a maioria das atividades muito complicado e não é necessário.

E o Google afirma-se, não é mesmo confiável.

Meu caminho é para salvar as alterações imediatamente nas preferências:

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

De alguma forma SharedPreferences trabalho semelhante como Bundles. E, naturalmente, e em primeiro desses valores têm de ser lidos a partir preferências.

No caso de dados complexos você pode usar SQLite em vez de usar preferências.

Ao aplicar este conceito, a atividade apenas continua a usar o último estado salvo, independentemente de que era uma inicial aberto com reinicializações no meio ou reabrir devido à pilha de volta.

Para responder à pergunta original diretamente. savedInstanceState é nulo, porque sua atividade é nunca ser re-criado.

A sua actividade só será recriada com um pacote de estado quando:

  • Configuração muda como alterar o idioma de orientação ou de telefone que pode requer uma nova instância de atividade a ser criado.
  • Você retorna para o aplicativo a partir do fundo após o OS destruiu a atividade.

Android vai destruir atividades em segundo plano quando está sob pressão de memória ou depois de terem sido em segundo plano por um período prolongado de tempo.

Ao testar o seu exemplo Olá mundo, existem algumas maneiras de sair e voltar à atividade.

  • Quando você pressiona o botão de volta a atividade está acabado. Re-lançamento do aplicativo é uma instância nova marca. Você não está retomando a partir do fundo em tudo.
  • Quando você pressiona o botão de casa ou usar o alternador de tarefas da Atividade vai para o fundo. Ao navegar de volta para a aplicação onCreate só vai ser chamado se a atividade teve que ser destruído.

Na maioria dos casos, se você está apenas pressionando casa e, em seguida, lançar o aplicativo novamente a atividade não precisa ser recriado. Ela já existe na memória para que onCreate () não será chamado.

Há uma opção em Configurações -> Opções do desenvolvedor chamado "Não manter actividades". Quando está habilitado Android será sempre destruir actividades e recriá-los quando eles estão em segundo plano. Esta é uma ótima opção para deixar habilitado ao desenvolver porque ele simula o pior cenário. (Um dispositivo de memória de baixo reciclagem de suas atividades o tempo todo).

As outras respostas são valiosas em que eles te ensinar as maneiras corretas para o estado de loja, mas eu não senti que eles realmente respondidas por que seu código não estava funcionando da maneira que você esperava.

Os métodos onSaveInstanceState(bundle) e onRestoreInstanceState(bundle) são úteis para a persistência de dados apenas durante a rotação da tela (mudança de orientação).
Eles não são ainda bem enquanto alternar entre aplicações (uma vez que o método onSaveInstanceState() é chamado, mas onCreate(bundle) e onRestoreInstanceState(bundle) não é invocado novamente.
Para mais preferências uso compartilhado persistência. ler este artigo

Meu problema era que I necessário persistência apenas durante o tempo de vida da aplicação (isto é, uma execução simples, incluindo a partir de outras sub-actividades no interior da mesma aplicação rotativa e o dispositivo etc). Tentei várias combinações de respostas acima, mas não consegui o que eu queria em todas as situações. No final o que funcionou para mim foi a obtenção de uma referência ao savedInstanceState durante onCreate:

mySavedInstanceState=savedInstanceState;

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

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

eu uso onSaveInstanceState onRestoreInstanceStateand como sugerido acima, mas acho que também ou em alternativa, poderia usar meu método para salvar a variável quando se altera (e.g. utilizando putBoolean)

Embora a resposta aceita é correto, não é um método mais rápido e mais fácil para salvar o estado Atividade no Android usando uma biblioteca chamada Icepick . Icepick é um processador de anotação que cuida de todo o código do clichê usado em salvar e restaurar estado para você.

Fazer algo como isso 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");
  }
}

Icepick irá trabalhar com qualquer objeto que salva seu estado com um Bundle.

Quando uma atividade é criada de onCreate () é chamado.

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

savedInstanceState é um objeto da classe Bundle que é nulo para o primeiro tempo, mas contém valores quando é recriado. Para salvar o estado da atividade que você tem para substituir onSaveInstanceState ().

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

colocar seus valores no objeto "outState" Bundle como outState.putString ( "chave", "Welcome Back") e salvar chamando super. Quando a atividade será destruído seu estado ser salvo em Bundle objeto e pode ser restaurada após recreação em onCreate () ou onRestoreInstanceState (). Bundle recebido em onCreate () e onRestoreInstanceState () são os mesmos.

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

Existem basicamente duas maneiras de implementar essa mudança.

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

Eu realmente não recomendo usar o segundo método. Uma vez que em um dos minha experiência, estava causando metade da tela preta dispositivo durante a rotação 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 que uma maneira em que você pode armazenar qualquer tipo de dados dentro de objeto de estado savedInstance.

Exemplo: Considere um caso se você quiser persistir objeto JSON. criar 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 onCreate e método onSaveInstanceState faça o seguinte. Ele será parecido com isto:

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

}

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

@VVK - Eu discordo parcialmente. Algumas maneiras de sair de um aplicativo não gatilho onSaveInstanceState (osis). Isso limita a utilidade de Osis. Está vale a pena apoiar, por recursos do sistema operacional mínimo, mas se um app quer retornar o usuário para o estado em que estavam, não importa como o aplicativo foi saiu, é necessário o uso de uma abordagem de armazenamento persistente em seu lugar. Eu uso onCreate para verificar pacote, e se ele estiver ausente, em seguida, verificar armazenamento persistente. Este centraliza a tomada de decisão. eu posso recuperar de um acidente, ou back saída botão ou menu personalizado item Sair ou voltar ao usuário a tela foi em muitos dias mais tarde. - ToolmakerSteve setembro 19 '15 às 10:38

código Kotlin:

Salvar:

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

e, em seguida, em onCreate() ou onRestoreInstanceState()

    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 quer ter Opcionais

Para obter os dados de estado de atividade armazenados em onCreate(), primeiro você tem que salvar os dados em savedInstanceState, substituindo método SaveInstanceState(Bundle savedInstanceState).

Quando a atividade destruir método SaveInstanceState(Bundle savedInstanceState) é chamado e lá você salvar os dados que você deseja salvar. E você começa mesmo em onCreate() quando a atividade reinício. (SavedInstanceState não vai ser nulo desde que guardou alguns dados nele antes da atividade são destruídas)

rápida simples de resolver este problema é usar Icepick

Em primeiro lugar, a instalação da 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 Estadual na Atividade

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 precisa para serializar seu estado em um Bundle (por exemplo, de argamassa ViewPresenters)

Icepick também pode gerar o código do estado 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
}

Não tenho certeza se a minha solução é desaprovada ou não, mas eu uso um serviço ligado a persistir o estado ViewModel. Se você armazená-lo na memória no serviço ou persistir e recuperá-lo a partir de um banco de dados SQLite depende de suas necessidades. Isto é o que os serviços de qualquer sabor fazer, eles fornecem serviços como a manutenção do estado do aplicativo e lógica de negócios comum abstrato.

Por causa da memória e processamento limitações inerentes em dispositivos móveis, eu trato vistas Android de uma maneira semelhante a uma página web. A página não manter o estado, é puramente um componente camada de apresentação, cujo único propósito é o estado do aplicativo presente e aceitar a entrada do usuário. Tendências recentes na web app arquitetura empregar o uso do modelo antigo, Vista, Controller (MVC), onde a página é a vista, os dados de domínio é o modelo eo controlador fica atrás de um serviço web. O mesmo padrão pode ser empregado em Android com o ser View, bem ... a vista, o modelo é seus dados de domínio eo controlador é implementado como um serviço vinculado Android. Sempre que você quiser a fim de interagir com o controlador, ligar para ele no início / currículo e unbind em stop / pause.

Esta abordagem dá-lhe a vantagem adicional de fazer cumprir a separação de princípio de design Preocupação em que todos vocês lógica de negócios do aplicativo pode ser movido para o seu serviço que reduz lógica duplicada em vários pontos de vista e permite a visão para impor um outro princípio importante do projeto, Responsabilidade Individual.

Agora Android fornece ViewModels para salvar o estado, você deve tentar ao uso que em vez de saveInstanceState.

O que para salvar eo que não?

Já se perguntou por que o texto no EditText é salvo automaticamente quando uma mudança de orientação? Bem, esta resposta é para você.

Quando uma instância de uma atividade é destruído eo Sistema recria uma nova instância (por exemplo, alteração de configuração). Ele tenta recriá-lo usando um conjunto de dados salvos de Estado Atividade de idade ( instância de estado ).

estado Instância é uma coleção de key-value pares armazenados em um objeto Bundle.

Por padrão Sistema salva a exibir os objetos no pacote, por exemplo.

  • Texto em EditText
  • posição de rolagem em um ListView, etc.

Se você precisa de outra variável a ser guardada como uma parte do estado da instância que deveria OVERRIDE método onSavedInstanceState(Bundle savedinstaneState).

Por exemplo, int currentScore em um GameActivity

Mais detalhes sobre o onSavedInstanceState (Bundle savedinstaneState), enquanto o salvamento de 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);
}

Então, por engano, se você esquecer de chamar comportamento padrão super.onSaveInstanceState(savedInstanceState);the não vai funcionar ou seja, texto em EditText não vai salvar.

Qual escolher para restaurar estado Atividade?

 onCreate(Bundle savedInstanceState)

ou

onRestoreInstanceState(Bundle savedInstanceState)

Ambos os métodos obter o mesmo objeto Bundle, por isso realmente não importa onde você escreve sua lógica de restauração. A única diferença é que no método onCreate(Bundle savedInstanceState) você terá que dar um cheque nulo, enquanto ele não é necessário neste último caso. Outras respostas têm já 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 chamada super.onRestoreInstanceState(savedInstanceState); para que o sistema restaurar a hierarquia de exibição por padrão

Bonus

O onSaveInstanceState(Bundle savedInstanceState) é invocado pelo sistema apenas quando o utilizador pretende voltar à atividade. Por exemplo, você está usando App X e de repente você recebe uma chamada. Você se move para o aplicativo de chamada e voltar ao X. aplicativo Neste caso, o método onSaveInstanceState(Bundle savedInstanceState) será invocado.

Mas considere isso, se um usuário pressiona o botão de volta. Supõe-se que o usuário não tem a intenção de voltar à atividade, portanto, neste caso onSaveInstanceState(Bundle savedInstanceState) não será invocado pelo sistema. Ponto é que você deve considerar todos os cenários ao salvar dados.

Links relevantes:

demo no comportamento padrão
Android Oficial Documentação .

Kotlin

Você deve substituir onSaveInstanceState e onRestoreInstanceState para armazenar e recuperar suas variáveis ??você quer ser persistente

Ciclo de vida gráfico

variáveis ??loja

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
}

Eu tenho uma idéia melhor. Isso seria melhor para salvar seus dados sem chamar onCreate novamente. Você pode desativá-lo a partir de atividade quando a orientação está mudando.

Em seu manifesto:

<activity android:name=".MainActivity"
        android:configChanges="orientation|screenSize">

Editar: esta solução não funcionará quando android mata o processo devido à falta de memória. se você precisa manter os dados com certeza, você precisa usar savedInstanceState neste caso, caso contrário, eu sugiro usar desta forma. arrefecer e fácil de usar!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top