Vra

Ek het op die Android SDK-platform gewerk, en dit is 'n bietjie onduidelik hoe om 'n toepassing se toestand te stoor.Gegewe hierdie geringe herbewerking van die 'Hallo, Android'-voorbeeld:

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

Ek het gedink dit sal genoeg wees vir die eenvoudigste geval, maar dit reageer altyd met die eerste boodskap, maak nie saak hoe ek van die toepassing af weg navigeer nie.

Ek is seker die oplossing is so eenvoudig soos oorheersing onPause of so iets, maar ek het vir 30 minute of so in die dokumentasie weggesteek en niks voor die hand gekry nie.

Was dit nuttig?

Oplossing

Jy moet onSaveInstanceState(Bundle savedInstanceState) ignoreer en skryf die aansoek staat waardes wat jy wil verander na die Bundle parameter soos volg:

@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.
}

Die bundel is in wese 'n manier van die stoor van 'n NVP ( "naam-waarde samestellings") kaart, en dit sal kry geslaag in te onCreate() en ook onRestoreInstanceState() waar jy dan sou onttrek die waardes soos volg:

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

Jy sal gewoonlik gebruik hierdie tegniek om byvoorbeeld waardes te stoor vir jou aansoek (keuses, ongeredde teks, ens.).

Ander wenke

Die savedInstanceState is slegs vir die redding van die staat wat verband hou met 'n huidige geval van 'n aktiwiteit, byvoorbeeld huidige navigasie of seleksie inligting, sodat as Android vernietig en herskep 'n aktiwiteit, dit kan terug kom soos dit was voor. Sien die dokumentasie vir onCreate en onSaveInstanceState

Vir meer lank geleef staat, oorweeg die gebruik van 'n sQLite databasis, 'n lêer of voorkeure. Sien Spaar Aanhoudende Staat .

Let daarop dat dit is NIE veilig om te gebruik onSaveInstanceState en onRestoreInstanceState vir aanhoudende data, volgens die dokumentasie oor Aktiwiteit state in http://developer.android.com/reference/android/app/Activity.html.

Die dokument lui (in die 'Aktiwiteitlewensiklus'-afdeling):

Let daarop dat dit belangrik is om te spaar aanhoudende data in onPause() Eerder Van onSaveInstanceState(Bundle) want die latere is nie deel van die lewensiklus terugroep, so sal nie wees nie geroep in elke situasie soos beskryf in sy dokumentasie.

Met ander woorde, plaas jou stoor/herstel-kode vir aanhoudende data in onPause() en onResume()!

EDIT:Vir verdere verduideliking, hier is die onSaveInstanceState() dokumentasie:

Hierdie metode word genoem voordat 'n aktiwiteit doodgemaak kan word, sodat wanneer dit kom 'n geruime tyd in die toekoms terug, dit kan sy toestand herstel.Vir byvoorbeeld, as aktiwiteit B voor aktiwiteit A van stapel gestuur word, en by sommige punt aktiwiteit A word doodgemaak om hulpbronne terug te eis, aktiwiteit A sal hê 'N Kans om die huidige toestand van sy gebruikerskoppelvlak via hierdie metode sodat wanneer die gebruiker terugkeer na aktiwiteit A, die toestand van die gebruikerskoppelvlak kan herstel word via onCreate(Bundle) of onRestoreInstanceState(Bundle).

My kollega het 'n artikel verduidelik aansoek staat op Android-toestelle, insluitende verduidelikings oor aktiwiteit lewensiklus en staat inligting, hoe om staat inligting te stoor, en spaar tot stand Bundle en SharedPreferences en 'n blik op hier .

Die artikel sluit drie benaderings:

Store plaaslike veranderlike / UI beheer data vir aansoek leeftyd (maw tydelik) met behulp van 'n geval staat bundel

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

Store plaaslike veranderlike / UI beheer data tussen toediening gevalle (maw permanent) Gebruik gedeel voorkeure

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

Hou voorwerp gevalle leef in geheue tussen aktiwiteite binne aansoek leeftyd met behulp van 'n behou nie-opset byvoorbeeld

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

Dit is 'n klassieke 'Gotcha "van Android ontwikkeling. Daar is twee kwessies hier:

  • Daar is 'n subtiele Android Framework fout wat grootliks bemoeilik aansoek stapel bestuur tydens ontwikkeling, ten minste op nalatenskap weergawes (nie heeltemal seker of / wanneer / hoe dit is vasgestel). Ek sal hierdie fout hieronder bespreek.
  • Die "normale" of bedoel manier om hierdie probleem te bestuur is, self, eerder ingewikkeld met die dualiteit van onPause / onResume en onSaveInstanceState / onRestoreInstanceState

navigeer oor al hierdie drade, ek vermoed dat baie van die tyd ontwikkelaars praat oor hierdie twee verskillende kwessies gelyktydig ... vandaar al die verwarring en verslae van "hierdie werk nie vir my".

In die eerste plek om die "bedoel" gedrag verduidelik: onSaveInstance en onRestoreInstance is broos en net vir verbygaande toestand. Die beoogde gebruik (afaict) is om aktiwiteit ontspanning hanteer wanneer die telefoon roteer (oriëntasie verandering). Met ander woorde, die beoogde gebruik is wanneer jou aktiwiteit is nog logies 'bo', maar nog steeds moet reinstantiated deur die stelsel. Die gered Bundel is nie volgehou buite die proses / geheue / gc, sodat jy kan nie regtig staatmaak op hierdie as jou aktiwiteit gaan na die agtergrond. Ja, miskien die geheue van jou aktiwiteit sal sy reis na die agtergrond te oorleef en te ontsnap GC, maar dit is nie betroubaar nie (of is dit voorspelbaar).

As jy 'n scenario waar daar betekenisvolle 'n gebruiker vordering 'of 'n staat wat gevolg moet word volgehou tussen' lanseer 'van jou aansoek, die leiding is om onPause en onResume gebruik. Jy moet kies en voor te berei 'n aanhoudende winkel jouself.

MAAR - daar is 'n baie verwarrende fout wat al hierdie dinge bemoeilik. Besonderhede is hier:

http://code.google.com/p/android/ kwessies / detail? id = 2373

http://code.google.com/p/android/ kwessies / detail? id = 5277

In beginsel, indien jou aansoek is van stapel gestuur met die vlag SingleTask, en dan later op jou dit van stapel te stuur vanaf die huis skerm of lanseerder menu, dan is dit die daaropvolgende aanroeping 'n NUWE taak sal skep ... jy sal effektief twee verskillende gevalle van jou jeug bewoon dieselfde stapel ... wat kry baie vreemd baie vinnig. Dit lyk om te gebeur wanneer jy jou program tydens ontwikkeling (dit wil sê vanaf Eclipse of Intellij) van stapel te stuur, so ontwikkelaars loop in hierdie baie. Maar ook deur 'n paar van die app store update meganismes (so dit 'n impak jou gebruikers sowel).

Ek het gesukkel deur hierdie drade vir ure voor ek besef dat my grootste probleem was hierdie fout, nie die beoogde raamwerk gedrag. 'N Groot writeup en tydelike oplossing (UPDATE: sien onder) blyk te wees van gebruiker @kaciula in hierdie antwoord:

Home sleutel pers gedrag

UPDATE Junie 2013 : Maande later, het ek uiteindelik gevind dat die "korrekte" oplossing. Jy hoef nie enige Stateful startedApp vlae jouself te bestuur, kan jy dit op te spoor van die raamwerk en borgtog gepas. Ek gebruik hierdie naby die begin van my 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 genoem wanneer die stelsel geheue nodig het en dood 'n aansoek. Dit is nie bekend as wanneer die gebruiker net sluit die aansoek. So ek dink aansoek staat moet ook gered word in onPause Dit moet gestoor word op 'n paar hardnekkige stoor soos Preferences of Sqlite

Beide metodes is nuttig en geldig en albei is die beste geskik vir verskillende scenario's:

  1. Die gebruiker beëindig die toepassing en maak dit op 'n latere datum weer oop, maar die toepassing moet data van die laaste sessie herlaai – dit vereis 'n aanhoudende bergingbenadering soos die gebruik van SQLite.
  2. Die gebruiker wissel van toepassing en kom dan terug na die oorspronklike en wil voortgaan waar hulle opgehou het - stoor en herstel bondeldata (soos toepassingstatusdata) in onSaveInstanceState() en onRestoreInstanceState() is gewoonlik voldoende.

As jy die staat data op 'n aanhoudende wyse stoor, kan dit herlaai word in 'n onResume() of onCreate() (of eintlik op enige lewensiklusoproep).Hierdie mag of mag nie gewenste gedrag wees nie.As jy dit in 'n bondel in 'n InstanceState, dan is dit verbygaande en is slegs geskik vir die stoor van data vir gebruik in dieselfde gebruiker 'sessie' (ek gebruik die term sessie losweg), maar nie tussen 'sessies' nie.

Dit is nie dat een benadering beter is as die ander nie, soos alles, is dit net belangrik om te verstaan ​​watter gedrag jy benodig en om die mees geskikte benadering te kies.

Besparing staat is 'n kludge op sy beste so ver as Ek is bekommerd. As jy nodig het om aanhoudende data te red, net gebruik 'n SQLite databasis. Android maak dit sooo maklik.

Iets soos hierdie:

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) {
        }
    }
}

'n eenvoudige oproep daarna

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

Ek dink ek het gevind dat die antwoord. Laat my vertel wat ek in eenvoudige woorde gedoen:

Veronderstel ek het twee aktiwiteite, activity1 en activity2 en ek opgevolg van activity1 om activity2 (Ek het 'n paar werke in activity2 gedoen) en weer terug na aktiwiteit 1 deur te kliek op 'n knoppie in activity1. Nou op hierdie stadium wou ek terug na activity2 gaan en ek wil my activity2 sien in dieselfde toestand as ek verlede links activity2.

Vir die bogenoemde scenario wat ek gedoen het, is dat in die openbaar Ek het 'n paar veranderinge soos hierdie:

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

En in die activity1 op die knoppie klik gebeurtenis wat ek gedoen het soos hierdie:

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

En in activity2 op die knoppie klik gebeurtenis wat ek gedoen het soos hierdie:

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

Nou wat sal gebeur is dat alles wat die veranderinge wat ons in die activity2 gemaak het nie verlore sal gaan, en ons kan activity2 sien in dieselfde toestand as wat ons voorheen links.

Ek glo dit is die antwoord en dit werk goed vir my. Korrek my as ek verkeerd is.

onSaveInstanceState() vir verbygaande data (herstel in onCreate()/onRestoreInstanceState()), onPause() vir aanhoudende data (herstel in onResume()).Van Android tegniese hulpbronne:

onSaveInstanceState() word deur Android geroep as die aktiwiteit gestop word en kan doodgemaak word voordat dit hervat word!Dit beteken dit moet enige toestand stoor wat nodig is om te herinitialiseer na dieselfde toestand wanneer die aktiwiteit herbegin word.Dit is die eweknie van die onCreate()-metode, en in werklikheid is die savedInstanceState-bundel wat aan onCreate() oorgedra is, dieselfde bundel wat jy as outState in die onSaveInstanceState()-metode konstrueer.

onPause() en op Hervat() is ook komplimentêre metodes.onPause() word altyd geroep wanneer die aktiwiteit eindig, selfs al het ons dit aangehits (byvoorbeeld met 'n finish()-oproep).Ons sal dit gebruik om die huidige nota terug na die databasis te stoor.Goeie praktyk is om ook enige hulpbronne vry te stel wat tydens 'n onPause() vrygestel kan word, om minder hulpbronne op te neem wanneer dit in die passiewe toestand is.

Baie onSaveInstance staat Callen wanneer die aktiwiteit gaan na agtergrond

Haal uit die dokumente: "Die metode onSaveInstanceState(Bundle) genoem voordat die aktiwiteit in so 'n agtergrond staat"

Om te help verminder boiler Ek gebruik die volgende interface en class om te lees / skryf na 'n Bundle vir die redding van byvoorbeeld staat.


In die eerste plek te skep 'n koppelvlak wat gebruik sal word om jou geval veranderlikes annoteer:

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 {

}

Toe, skep 'n klas waar besinning sal gebruik word om waardes aan die bundel te red:

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

}

Voorbeeld gebruik:

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: Hierdie kode is aangepas uit 'n biblioteek projek met die naam AndroidAutowire wat onder die volgende lisensie < a href = "https://raw.githubusercontent.com/CardinalNow/AndroidAutowire/master/LICENSE"> MIT lisensie .

Intussen Ek doen in die algemeen nie meer gebruik

Bundle savedInstanceState & Co

Die lewensiklus is vir die meeste aktiwiteite te ingewikkeld en nie nodig nie.

En Google bepaal self, dit is nie eens betroubaar.

My manier is om enige veranderinge onmiddellik in die voorkeure te red:

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

In een of ander manier SharedPreferences werk soortgelyk soos Bundels. En natuurlik en met die eerste sodanige waardes het om gelees te word van voorkeure.

In die geval van komplekse data jy kan SQLite gebruik in plaas van die gebruik van voorkeure.

By die toepassing van hierdie konsep, die aktiwiteit net voort om die laaste gered staat gebruik, ongeag of dit was 'n aanvanklike oop met herselflaaie tussen of 'n heropen as gevolg van die weer stapel.

Om die oorspronklike vraag direk te beantwoord.savedInstancestate is nul omdat jou aktiwiteit nooit herskep word nie.

Jou aktiwiteit sal slegs met 'n staatsbondel herskep word wanneer:

  • Konfigurasieveranderinge soos die verandering van die oriëntasie of telefoontaal wat dalk vereis dat 'n nuwe aktiwiteitsinstansie geskep moet word.
  • Jy keer terug na die toepassing vanaf die agtergrond nadat die bedryfstelsel die aktiwiteit vernietig het.

Android sal agtergrondaktiwiteite vernietig wanneer onder geheuedruk of nadat hulle vir 'n lang tydperk op die agtergrond was.

Wanneer jy jou hallo wêreld-voorbeeld toets, is daar 'n paar maniere om te verlaat en terug te keer na die Aktiwiteit.

  • Wanneer jy die terugknoppie druk, is die aktiwiteit voltooi.Die herbekendstelling van die toepassing is 'n splinternuwe geval.Jy hervat glad nie van die agtergrond af nie.
  • Wanneer jy die tuisknoppie druk of die taakwisselaar gebruik, sal die aktiwiteit op die agtergrond gaan.Wanneer daar terug na die toepassing navigeer word, sal onCreate slegs geroep word as die aktiwiteit vernietig moes word.

In die meeste gevalle, as jy net huis toe druk en dan die toepassing weer begin, hoef die aktiwiteit nie herskep te word nie.Dit bestaan ​​reeds in die geheue, so onCreate() sal nie genoem word nie.

Daar is 'n opsie onder Instellings -> Ontwikkelaaropsies genaamd "Moenie aktiwiteite hou nie".As dit geaktiveer is, sal Android altyd aktiwiteite vernietig en dit herskep wanneer hulle 'n agtergrond het.Dit is 'n goeie opsie om aangeskakel te laat wanneer jy ontwikkel, want dit simuleer die ergste scenario.('n Lae geheue toestel wat jou aktiwiteite heeltyd herwin).

Die ander antwoorde is waardevol omdat hulle jou die korrekte maniere leer om toestand te stoor, maar ek het nie gevoel hulle het regtig geantwoord WAAROM jou kode nie werk soos jy verwag het nie.

Die onSaveInstanceState(bundle) en onRestoreInstanceState(bundle) metodes is nuttig vir data volharding bloot terwyl die draai van die skerm (oriëntasie verandering).
Hulle is nie eens 'n goeie tydens wisseling tussen aansoeke (sedert die onSaveInstanceState() metode genoem, maar onCreate(bundle) en onRestoreInstanceState(bundle) is nie weer opgeroep.
Vir meer volharding gebruik gedeel voorkeure. lees hierdie artikel

My probleem was dat ek nodig het volharding net tydens die aansoek leeftyd (dit wil sê 'n enkele uitvoering insluitend begin ander sub-aktiwiteite binne dieselfde app en draai die toestel ens). Ek het probeer om verskeie kombinasies van bogenoemde antwoorde, maar het nie kry wat ek wou in alle situasies. Op die ou end wat vir my gewerk het was 'n verwysing na die savedInstanceState tydens OnCreate verkry:

mySavedInstanceState=savedInstanceState;

en gebruik dit om die inhoud van my veranderlike verkry wanneer ek dit nodig gehad het, in die trant van:

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

Ek gebruik onSaveInstanceStateand onRestoreInstanceState soos hierbo voorgestel, maar ek dink ek kan ook of alternatiewelik my metode gebruik om die veranderlike te red wanneer dit verander (bv die gebruik van putBoolean)

Hoewel die aanvaarde antwoord korrek is, is daar 'n vinniger en makliker manier om die aktiwiteit staat op Android te red met behulp van 'n biblioteek genoem Icepick . Icepick is 'n aantekening verwerker wat sorg vir al die boiler kode gebruik in besparing en die herstel van die staat vir jou.

so iets te doen met 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);
  }
}

is dieselfde as om dit te doen:

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 sal saam met enige voorwerp wat sy toestand spaar met 'n Bundle.

As 'n aktiwiteit is geskep dit OnCreate () metode genoem word.

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

savedInstanceState is 'n voorwerp van Bundel klas wat nul vir die eerste keer, maar dit bevat waardes wanneer dit herskep. Om aktiwiteit se toestand te red wat jy hoef te ignoreer onSaveInstanceState ().

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

sit jou waardes in "outState" Bundel voorwerp soos outState.putString ( "sleutel", "Welkom Terug") en red deur te bel super. Wanneer aktiwiteit sal vernietig dit staat gered in Bundel voorwerp en kan herstel word na ontspanning in OnCreate () of onRestoreInstanceState (). Bundel ontvang in OnCreate () en onRestoreInstanceState () is dieselfde.

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

of

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

Daar is basies twee maniere om hierdie verandering te implementeer.

  1. die gebruik van onSaveInstanceState() en onRestoreInstanceState().
  2. In die lig android:configChanges="orientation|screenSize".

Ek het regtig nie aanbeveel tweede metode gebruik. Sedert in een van my ondervinding was dit veroorsaak die helfte van die toestel skerm swart terwyl roterende van portret na landskap en omgekeerd.

Die gebruik van die eerste metode hierbo genoem, kan ons data voortduur wanneer oriëntasie verander of enige config verandering gebeur. Ek weet 'n manier waarop jy enige tipe van data binne savedInstance staat voorwerp kan stoor.

Voorbeeld: Dink aan 'n geval as jy wil into voorwerp voortduur. skep 'n model klas met getters en etters.

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

Nou in jou aktiwiteit in OnCreate en onSaveInstanceState metode doen die volgende. Dit sal lyk iets soos hierdie:

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

}

Hier is 'n opmerking van Steve Moseley se antwoord (deur ToolmakerSteve ) wat dinge sit in perspektief (in die hele onSaveInstanceState vs onPause, oos koste vs weste koste-sage )

  

@VVK - ek gedeeltelik saamstem nie. Sommige maniere verlaat 'n app doen nie sneller   onSaveInstanceState (OSIS). Dit beperk die nut van OSIS. sy   die moeite werd om te ondersteun, vir 'n minimale hulpbronne OS, maar as 'n app wil   terug te keer die gebruiker in staat om die toestand waarin dit was, maak nie saak hoe die jeug was   opgewonde, is dit nodig om 'n aanhoudende stoor benadering in plaas gebruik.    Ek gebruik OnCreate om te kyk vir bondel, en as dit ontbreek, dan check    aanhoudende stoor. Dit centraliseert die besluitneming. ek kan   herstel van 'n ongeluk, of terug knoppie uitgang of persoonlike menu-item afrit, of   kom terug na die skerm gebruiker was op baie dae later. - ToolmakerSteve September   19 '15 by 10:38

Kotlin kode:

red:

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

en dan in onCreate() of 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

Voeg verstek waardes as jy nie wil hê dat Optionals het

Om aktiwiteit staat data gestoor in onCreate() kry, eerste moet jy data in savedInstanceState red deur oorheersende SaveInstanceState(Bundle savedInstanceState) metode.

Wanneer aktiwiteit te vernietig SaveInstanceState(Bundle savedInstanceState) metode kry geroep en daar sal jy data wat jy wil red red. En jy dieselfde in onCreate() kry wanneer aktiwiteit herlaai. (SavedInstanceState gewoond nul wees aangesien jy 'n paar data in dit gespaar voor aktiwiteit kry vernietig)

Eenvoudige vinnig om hierdie probleem op te los met behulp van IcePick

In die eerste plek die opstel van die biblioteek in app/build.gradle

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

Nou, kom ons kyk hierdie voorbeeld hieronder hoe om staat in aktiwiteit te red

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

Dit werk vir aktiwiteite, Fragments of enige voorwerp wat nodig het om sy toestand afleveringen op 'n bondel (ViewPresenters bv mortier se)

Icepick kan ook genereer die geval staat kode vir persoonlike bestaan:

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
}

Nie seker of my oplossing is afgekeur of nie, maar ek gebruik 'n gebonde diens aan ViewModel staat voortduur. Of jy dit stoor in die geheue in die diens of voortduur en haal dit uit 'n sQLite databasis hang af van jou behoeftes. Dit is wat dienste van enige geur doen, wat hulle lewer dienste soos die handhawing van toepassing staat en abstrakte selfstandige sake-logika.

As gevolg van die geheue en die verwerking van beperkings inherent op mobiele toestelle, ek behandel Android standpunte in 'n soortgelyke manier om 'n web bladsy. Die bladsy is nie in stand te hou toestand, dit is bloot 'n aanbieding laag komponent wie se enigste doel is om aansoek staat stel en aanvaar die gebruiker se toevoer. Onlangse tendense in web app argitektuur in diens van die gebruik van die eeue-oue Model, View, Controller (MVC) patroon, waar die bladsy is die View, domein data is die model, en die kontroles sit agter 'n web diens. Dieselfde patroon kan in diens geneem word in Android met die View wese, goed ... die View, die model is jou domein data, en die Kontroleur geïmplementeer as 'n Android-gebonde diens. Wanneer jy 'n oog op die interaksie met die bestuurder wil, bind om dit op start / CV en ontbind op stop / pouse.

Hierdie benadering gee jou die ekstra bonus van die handhawing van die skeiding Kommer ontwerp beginsel van in dat julle almal aansoek besigheid logika in jou diens wat gedupliseer logika oor verskeie standpunte verminder en laat die oog op 'n ander belangrike ontwerpbeginsel dwing verskuif kan word, enkele verantwoordelikheid.

Nou Android bied ViewModels vir die redding van die staat, jy moet probeer om te gebruik in plaas van saveInstanceState.

Wat om te spaar en wat nie?

Al ooit gewonder hoekom die teks in die EditText word outomaties gestoor terwyl 'n oriëntasie verander?Wel, hierdie antwoord is vir jou.

Wanneer 'n instansie van 'n aktiwiteit vernietig word en die stelsel herskep 'n nuwe instansie (byvoorbeeld, konfigurasieverandering).Dit probeer om dit te herskep met behulp van 'n stel gestoorde data van ou Aktiwiteitstoestand (instansie staat).

Instance state is 'n versameling van sleutel-waarde pare gestoor in 'n Bundle voorwerp.

By verstek stoor System byvoorbeeld die View-objekte in die Bundel.

  • Teks in EditText
  • Rollees posisie in 'n ListView, ens.

As jy 'n ander veranderlike nodig het om as 'n deel van instansie gestoor te word, moet jy OORSKRYF onSavedInstanceState(Bundle savedinstaneState) metode.

Byvoorbeeld, int currentScore in 'n spelaktiwiteit

Meer besonderhede oor die onSavedInstanceState(Bundle savedinstaneState) terwyl data gestoor word

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

So per ongeluk as jy vergeet om te bel super.onSaveInstanceState(savedInstanceState);Die standaardgedrag sal nie werk nie, dws Teks in EditText sal nie stoor nie.

Wat om te kies om die aktiwiteitstatus te herstel?

 onCreate(Bundle savedInstanceState)

OF

onRestoreInstanceState(Bundle savedInstanceState)

Albei metodes kry dieselfde Bundle-voorwerp, so dit maak nie regtig saak waar jy jou herstellogika skryf nie.Die enigste verskil is dat in onCreate(Bundle savedInstanceState) metode sal jy 'n nultjek moet gee terwyl dit in laasgenoemde geval nie nodig is nie.Ander antwoorde het reeds kodebrokkies.Jy kan hulle verwys.

Meer besonderhede oor die 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);
}

Bel altyd super.onRestoreInstanceState(savedInstanceState); sodat Stelsel die View-hiërargie by verstek herstel

Bonus

Die onSaveInstanceState(Bundle savedInstanceState) word slegs deur die stelsel opgeroep wanneer die gebruiker van voorneme is om terug te keer na die Aktiwiteit.Byvoorbeeld, jy gebruik App X en skielik kry jy 'n oproep.Jy beweeg na die beller-toepassing en kom terug na die toepassing X.In hierdie geval die onSaveInstanceState(Bundle savedInstanceState) metode aangeroep sal word.

Maar oorweeg dit as 'n gebruiker die terugknoppie druk.Daar word aanvaar dat die gebruiker nie van plan is om terug te keer na die Aktiwiteit nie, dus in hierdie geval onSaveInstanceState(Bundle savedInstanceState) sal nie deur die stelsel opgeroep word nie.Die punt is dat jy al die scenario's moet oorweeg terwyl jy data stoor.

Relevante skakels:

Demo oor verstekgedrag
Android Amptelike Dokumentasie.

Kotlin

Jy moet onSaveInstanceState en onRestoreInstanceState oorheers te slaan en haal jou veranderlikes wat jy wil aanhoudende te wees

Lewensiklus grafiek

Store veranderlikes

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

haal veranderlikes

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
}

Ek het 'n beter idee. Dit sou beter wees om jou data te red sonder OnCreate weer 'n beroep. Jy kan dit vanaf aktiwiteit uit te skakel wanneer oriëntasie verander.

In jou openbaar:

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

Edit: hierdie oplossing sal nie werk nie wanneer Android doodslaan die proses as gevolg van lae geheue. As jy nodig het om data vir seker te hou, moet jy SavedInstanceState gebruik in hierdie geval, anders Ek stel voor die gebruik van hierdie manier. koel en maklik om te gebruik!

Gelisensieer onder: CC-BY-SA met toeskrywing
Nie verbonde aan StackOverflow
scroll top