Kaydetme örneği durumunu kullanarak bir etkinlik durumunu nasıl kaydedebilirim?


2620

Android SDK platformunda çalışıyorum ve bir uygulamanın durumunu nasıl kaydedeceğimiz biraz belirsiz. 'Merhaba, Android' örneğinin bu küçük yeniden kullanımı göz önüne alındığında:

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

En basit durum için yeterli olacağını düşündüm, ancak uygulamadan nasıl uzaklaştığım önemli değil, her zaman ilk mesajla yanıt verir.

Çözümün geçersiz kılma onPauseveya bunun gibi bir şey kadar basit olduğuna eminim , ancak 30 dakikadır dokümantasyondan uzak duruyorum ve açık bir şey bulamadım.


9
SaveInstanceState == null ne zaman ve ne zaman null değil?
Trojan.ZBOT

90
Etkinliğinizi açıkça söylediğiniz gibi - söylediğiniz gibi, geri gitmek gibi, ondan uzaklaşarak. Aslında, bu 'saveInstanceState'in kullanıldığı senaryo, Android etkinliğinizi rekreasyon için yok ettiğinde ortaya çıkar. Intance için: Etkinlik çalışırken telefonunuzun dilini değiştirirseniz (ve böylece projenizden farklı kaynakların yüklenmesi gerekir). Başka bir yaygın senaryo, etkinliğin yeniden oluşturulması ve manzarada görüntülenmesi için telefonunuzu yana döndürdüğünüz durumdur.
villoren

16
İkinci mesajı almak için geliştirici seçeneklerinde "Aktiviteleri tutma" seçeneğini etkinleştirin. Bir ana sayfa düğmesine basın ve sonrakilerden geri dönün.
Yaroslav Mytkalyk

5
Bu oldukça faydalı bir geliştirici.android.com
training/basics/

6
bunu aşağıdakilerle yapabilirsiniz: onSaveInstanceState (Bundle savedInstanceState)
VahidHoseini

Yanıtlar:


2568

Aşağıdaki gibi parametreye onSaveInstanceState(Bundle savedInstanceState)değiştirmek istediğiniz uygulama durumu değerlerini geçersiz kılmanız ve yazmanız gerekir Bundle:

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

Paketi aslında bir gebelikte bulantı ve kusmanın ( "Ad-Değer Çifti") harita saklamanın bir yoludur ve bunun için geçirilen alacak onCreate()da ve onRestoreInstanceState()daha sonra böyle aktivite değerleri ayıklamak nerede:

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

Veya bir parçadan.

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

Bu tekniği genellikle uygulamanız için örnek değerleri (seçimler, kaydedilmemiş metin vb.) Saklamak için kullanırsınız.


24
Bunun telefonda ya da öykünücüde çalışma şansı var mı? Boş olmayan bir saveInstanceState alamadım.
Adam Jack

491
DİKKATLİ: Paketinize değerlerinizi eklemeden önce super.onSaveInstanceState (savedInstanceState) öğesini çağırmanız gerekir, aksi takdirde bu çağrıda silinirler (Droid X Android 2.2).
jkschneider

121
Dikkat: Resmi belgeler, onPause-Metodu içindeki önemli bilgileri kaydetmeniz gerektiğini belirtir, çünkü onaveinstance-yöntemi android yaşam döngüsünün bir parçası değildir. developer.android.com/reference/android/app/Activity.html
schlingel

32
Bu gerçek, onSaveInstanceStateyalnızca ekran yönü değişiklikleri dışında neredeyse işe yaramaz hale gelir . Hemen hemen tüm diğer durumlarda, ona asla güvenemezsiniz ve UI durumunuzu başka bir yere manuel olarak kaydetmeniz gerekir. Veya GERİ düğmesi davranışını geçersiz kılarak uygulamanızın öldürülmesini önleyin. İlk etapta neden böyle uyguladıklarını anlamıyorum. Tamamen kasıtsız. Ve bu Paketin size bu çok özel yöntem dışında bir şeyleri kaydetmenize izin vermesini sağlayamazsınız.
chakrit

12
Not o / Demet üzerine / UI durumunu geri yüklüyor tasarrufu otomatik halledilir için Viewatanan kimlikleri olmuştur s . Gönderen onSaveInstanceStatedocs: "Varsayılan uygulama arayarak sizin için UI örnek başına devletin en ilgilenir onSaveInstanceState(), kimliğine sahipse hiyerarşi içinde her görüntülemede ve id kaydederek, şu anda geri hepsi görünümü (duruldu varsayılan uygulaması ile onRestoreInstanceState(Bundle)) "
Vicky Chijwani

433

Bu savedInstanceState, yalnızca bir Etkinliğin geçerli bir örneğiyle, örneğin geçerli gezinme veya seçim bilgileriyle ilişkili durumun kaydedilmesi içindir, böylece Android bir Etkinliği yok edip yeniden oluşturursa, eskisi gibi geri gelebilir. Belgelerine bakın onCreateveonSaveInstanceState

Daha uzun ömürlü bir durum için, bir SQLite veritabanı, bir dosya veya tercihler kullanmayı düşünün. Bkz . Kalıcı Durumu Kaydetme .


3
SaveInstanceState == null ne zaman ve ne zaman null değil?
Trojan.ZBOT

6
saveInstanceState, sistem Etkinliğinizin yeni bir örneğini oluştururken null olur ve geri yüklerken null değildir.
Gabriel Câmara

7
... sistemin ne zaman yeni bir Etkinlik örneği oluşturması gerektiği sorusunu gündeme getirir . Bir uygulamadan çıkmanın bazı yolları bir paket oluşturmaz, bu nedenle yeni bir örnek oluşturulması gerekir. Temel sorun budur; bu , demetin varlığına güvenemeyeceği anlamına gelir ve kalıcı depolama için bazı alternatif araçlar yapmak zorundadır. OnSave / onRestoreInstanceState'in yararı, sistemin fazla sistem kaynağı tüketmeden aniden yapabileceği bir mekanizma olmasıdır . Bu nedenle, uygulamadan daha zarif bir çıkış için kalıcı depolama alanına sahip olmanın yanı sıra bunu desteklemek de iyidir.
22:24

415

Http://developer.android.com/reference/android/app/Activity.html adresindeki Etkinlik durumlarıyla ilgili belgelere göre, güvenli ve kalıcı verilerin KULLANILMAMASINA dikkat edin .onSaveInstanceStateonRestoreInstanceState

Dokümanın durumu ('Etkinlik Yaşam Döngüsü' bölümünde):

O kalıcı verileri kaydetmek için önemli olduğunu Not onPause()yerine onSaveInstanceState(Bundle) sonradan yaşam döngüsü geri aramaları bir parçası değildir, çünkü onun belgelerinde açıklandığı şekilde, bu yüzden her durumda denilen edilmeyecektir.

Başka bir deyişle, kalıcı veriler için kaydetme / geri yükleme kodunuzu onPause()ve onResume()!

DÜZENLEME : Daha fazla açıklama için işte onSaveInstanceState()belgeler:

Bu yöntem, bir faaliyet öldürülmeden önce çağrılır, böylece gelecekte bir süre sonra geri geldiğinde durumunu geri yükleyebilir. Örneğin, B aktivitesi A aktivitesinin önünde başlatılırsa ve bir noktada A aktivitesi kaynakları geri almak için öldürülürse, A aktivitesi, kullanıcı geri döndüğünde bu yöntemle kullanıcı arayüzünün mevcut durumunu kaydetme şansına sahip olacaktır. A etkinliğine, kullanıcı arabiriminin durumu onCreate(Bundle)veya ile geri yüklenebilir onRestoreInstanceState(Bundle).


55
Sadece nitpick için: güvenli de değil. Bu sadece neyi korumak istediğinize ve ne kadar süreyle bağlı olduğuna bağlıdır, @Bernard orijinal sorusunda tamamen açık değildir. InstanceState, mevcut kullanıcı arayüzü durumunu (kontrollere girilen veriler, listelerdeki mevcut konumlar vb.) Korumak için mükemmelken Duraklat / Devam Et, uzun süreli kalıcı depolama için tek olasılıktır.
Pontus Gagge

30
Bu düşürülmelidir. (Kaydet | Geri Yükle) InstanceState üzerinde yaşam döngüsü yöntemleri gibi kullanmak güvenli değildir (örneğin, durumu kaydetmek / geri yüklemek yerine başka bir şey yapmak). Durumu kaydetmek / geri yüklemek için mükemmel derecede iyidirler. Ayrıca, onPause ve onResume'de durumu nasıl kaydetmek / geri yüklemek istersiniz? Kullanabileceğiniz bu yöntemlerde Bundles almazsınız, bu nedenle aptal olan veritabanlarında, dosyalarda vb. Diğer bazı devlet tasarruflarını kullanmanız gerekir.
Felix

141
Bu kişiyi en azından o belgeleri gözden geçirmek için çaba sarf etmemeliyiz ve bence insanlar aslında bilgili bir topluluk oluşturmak ve birbirlerine AŞAĞI DEĞİL değil yardım etmek için buradayız. bu yüzden 1 çaba için oy ve ben aşağı oy yerine değil oy ya da yok .... sizden isteyeceğim .... Bu kişi belgelerin üzerinden gitmek istiyorum karışıklık temizlemek. 1 oy verildi :)
AZ_

21
Bu cevabın bir aşağı oyu hak ettiğini düşünmüyorum. En azından cevaplamak için çaba gösterdi ve doco'dan bir bölüm alıntıladı.
GSree

34
Bu cevap kesinlikle doğrudur ve YUKARI oylamayı hak ediyor, aşağı değil! Görmeyenler için devletler arasındaki farkı netleştireyim. Seçilen radyo düğmeleri ve giriş alanındaki bazı metinler gibi bir GUI durumu, ListView'da görüntülenen bir listeye eklenen kayıtlar gibi veri durumundan çok daha az önemlidir. İkincisi onPause'da veritabanına kaydedilmelidir çünkü tek garantili çağrıdır. Bunun yerine onSaveInstanceState öğesine koyarsanız, çağrılmazsa veri kaybetme riskiyle karşı karşıya kalırsınız. Ancak radyo düğmesi seçimi aynı nedenden dolayı kaydedilmezse - bu büyük bir sorun değildir.
JBM

206

Meslektaşım durum bilgilerini saklamak için nasıl faaliyet ömrü ve devlet bilgilere açıklamaların yer aldığı Android cihazlarında uygulama durumunu açıklayan bir makale yazdım ve devlete tasarruf Bundleve SharedPreferencesve burada bir göz atın .

Makale üç yaklaşımı kapsıyor:

Bir örnek durum paketi kullanarak uygulama ömrü boyunca (yani geçici olarak) yerel değişken / kullanıcı arabirimi denetim verilerini depolayın

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

Paylaşılan tercihleri ​​kullanarak yerel değişken / UI kontrol verilerini uygulama örnekleri arasında (yani kalıcı olarak) saklayın

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

Korunan bir yapılandırma dışı örneği kullanarak, uygulama yaşam süresi içindeki etkinlikler arasındaki nesne örneklerini bellekte tutma

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

3
@ MartinBelcher-Eigo Makale SharedPreferences veri "Bu veri cihazdaki veritabanına yazılmıştır .." diyor Verilerin uygulamanın dosya sisteminin dosya dizininde bir dosyada saklandığını düşünüyorum.
Tom

2
@Tom SharefPrefs verileri xml dosyasına yazılır. Xml bir tür veritabanı mı? Bunu söyleyebilirim;)
MaciejGórski

148

Bu Android geliştirme klasik bir 'yakaladım'. Burada iki sorun var:

  • En azından eski sürümlerde, geliştirme sırasında uygulama yığını yönetimini büyük ölçüde karmaşıklaştıran ince bir Android Framework hatası var (tamamen / ne zaman / nasıl düzeltildiğinden tam olarak emin değilim). Bu hatayı aşağıda tartışacağım.
  • Bu sorunu yönetmenin 'normal' veya amaçlanan yolu, onPause / onResume ve onSaveInstanceState / onRestoreInstanceState'in ikiliği ile oldukça karmaşıktır.

Tüm bu konulara göz atarken, geliştiricilerin çoğu zaman bu iki farklı konu hakkında aynı anda konuştuğundan şüpheleniyorum ... bu nedenle "bu benim için işe yaramaz" konusunun tüm karışıklıkları ve raporları.

İlk olarak, 'amaçlanan' davranışı açıklığa kavuşturmak için: onSaveInstance ve onRestoreInstance kırılgandır ve yalnızca geçici durum içindir. Kullanım amacı (afaict), telefon döndürüldüğünde Aktivite rekreasyonunu ele almaktır (yön değiştirme). Başka bir deyişle, amaçlanan kullanım Faaliyetinizin hala mantıksal olarak 'üstte' olduğu, ancak yine de sistem tarafından yeniden başlatılması gerektiğidir. Kaydedilen Paket işlem / bellek / gc'nin dışında kalmaz, bu nedenle etkinliğiniz arka plana giderse buna gerçekten güvenemezsiniz. Evet, belki de Faaliyetinizin hafızası arka plandaki yolculuğundan kurtulur ve GC'den kaçar, ancak bu güvenilir değildir (ne de öngörülebilir değildir).

Dolayısıyla, uygulamanızın 'lansmanları' arasında kalıcı olması gereken anlamlı bir 'kullanıcı ilerlemesi' veya durumunun olduğu bir senaryo varsa, kılavuz onPause ve onResume kullanmaktır. Kalıcı bir mağazayı kendiniz seçmeli ve hazırlamalısınız.

AMA - tüm bunları karmaşıklaştıran çok kafa karıştırıcı bir hata var. Detaylar burada:

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

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

Temel olarak, uygulamanız SingleTask bayrağıyla başlatılır ve daha sonra onu ana ekrandan veya başlatıcı menüsünden başlatırsanız, sonraki çağrılar YENİ bir görev oluşturur ... uygulamanızın iki farklı örneğini etkili bir şekilde kullanırsınız. aynı yığını yaşıyor ... ki bu çok garip çok hızlı oluyor. Bu, uygulamanızı geliştirme sırasında (yani Eclipse veya Intellij'den) başlattığınızda gerçekleşiyor gibi görünüyor, bu nedenle geliştiriciler buna çok fazla giriyor. Ancak bazı uygulama mağazası güncelleme mekanizmaları aracılığıyla (böylece kullanıcılarınızı da etkiler).

Asıl sorunumun amaçlanan çerçeve davranışı değil, bu hata olduğunu fark etmeden önce bu konuları saatlerce savaştım. Harika bir yazı vegeçici çözüm (GÜNCELLEME: aşağıya bakın) bu yanıtta @kaciula kullanıcısından geliyor gibi görünüyor:

Ana tuşa basma davranışı

GÜNCELLEME Haziran 2013 : Aylar sonra nihayet 'doğru' çözümü buldum. Herhangi bir durumdaki başlatılan Uygulama bayraklarını kendiniz yönetmeniz gerekmez, bunu çerçeveden algılayabilir ve uygun şekilde kefalet edebilirsiniz. Bunu LauncherActivity.onCreate'ımın başlangıcına yakın kullanıyorum:

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

87

onSaveInstanceStatesistem belleğe ihtiyaç duyduğunda ve bir uygulamayı öldürdüğünde çağrılır. Kullanıcı uygulamayı kapattığında çağrılmaz. Ben de uygulama durumu da kaydedilebilir gerektiğini düşünüyorum Yani onPausebu gibi bazı Kalıcı belleğe kaydedilmelidir PreferencesveyaSqlite


36
Üzgünüm, bu pek doğru değil. Etkinliğin yeniden yapılması gerekmeden önce onSaveInstanceState çağrılır. yani kullanıcı cihazı her döndürdüğünde. Geçici görüş durumlarının depolanması içindir. Android uygulamayı kapanmaya zorladığında, onSaveInstanceState aslında çağrılmaz (bu nedenle önemli uygulama verilerini saklamak için güvensizdir). onPause, ancak etkinlik öldürülmeden önce çağrılması garanti edilir, bu nedenle kalıcı bilgileri tercihlerde veya Squlite'de saklamak için kullanılmalıdır. Doğru cevap, yanlış nedenler.
moveaway00

74

Her iki yöntem de faydalı ve geçerlidir ve her ikisi de farklı senaryolar için en uygun yöntemdir:

  1. Kullanıcı uygulamayı sonlandırır ve daha sonraki bir tarihte yeniden açar, ancak uygulamanın son oturumdan verileri yeniden yüklemesi gerekir - bu, SQLite kullanımı gibi kalıcı bir depolama yaklaşımı gerektirir.
  2. Kullanıcı uygulamayı değiştirir ve sonra orijinaline geri döner ve kaldığı yerden almak ister - paket verilerini (uygulama durumu verileri gibi) kaydedin ve geri yükleyin onSaveInstanceState()ve onRestoreInstanceState()genellikle yeterlidir.

Durum verilerini kalıcı bir şekilde kaydederseniz, bir onResume()veya onCreate()(veya aslında herhangi bir yaşam döngüsü çağrısında) yeniden yüklenebilir . Bu istenen davranış olabilir veya olmayabilir. Bir pakette bir pakette saklarsanız InstanceState, geçici olur ve yalnızca aynı kullanıcı 'oturumunda (oturum terimini gevşek kullanıyorum) kullanmak için veri depolamak için uygundur, ancak' oturumlar 'arasında değil.

Bir yaklaşım diğerinden daha iyi değildir, her şey gibi, hangi davranışa ihtiyacınız olduğunu anlamak ve en uygun yaklaşımı seçmek önemlidir.


70

En iyi durumda olduğumdan tasarruf etmek en iyi ihtimalle bir çamurdur. Kalıcı verileri kaydetmeniz gerekiyorsa, bir SQLite veritabanı kullanın . Android SOOO'yu kolaylaştırıyor.

Bunun gibi bir şey:

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

Bundan sonra basit bir çağrı

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

9
Bir SQLite veritabanının yüklenmesi çok uzun sürdüğü için, bunun kullanıcıya uygulamanın kullanıcı arayüzünü göstermek için kritik bir yol olduğunu düşünüyoruz. Aslında zamanlamadı, bu yüzden düzeltildiğim için mutluyum, ama kesinlikle bir veritabanı dosyasını yüklemek ve açmak hızlı olmayacak mı?
Tom

5
Bir acemi uygulaması kesip yapıştırın ve hemen kullanabileceğiniz bir çözüm sağladığınız için çok teşekkür ederiz! @Tom Hız ilerledikçe 1000 çiftin saklanması yaklaşık yedi saniye sürer, ancak bunu bir AsyncTask'ta yapabilirsiniz. Ancak, bir nihayet {cursor.close ()} eklemeniz gerekir, aksi takdirde bunu yaparken bellek sızıntısı olur.
Noumenon

3
Buna rastladım ve düzgün görünse de, son zamanlarda / birlikte çalıştığım cihaz olan Google Glass'da kullanmayı denemekten çekinmeyin.
Stephen Tetreault

61

Sanırım cevabı buldum. Ne yaptığımı basit sözlerle anlatayım:

Etkinlik1 ve etkinlik2 olmak üzere iki etkinliğim olduğunu ve etkinlik1'deki etkinlik2'ye (etkinlik2'de bazı işler yaptım) ve etkinlik1'deki bir düğmeyi tıklatarak etkinlik 1'e geri döndüğümü varsayalım. Şimdi bu aşamada etkinlik2'ye geri dönmek istedim ve etkinlik2'yi son etkinlik2'den ayrıldığımda aynı durumda görmek istiyorum.

Yukarıdaki senaryo için yaptığım şey, manifestte böyle bazı değişiklikler yaptığımdır:

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

Ve button1 olayındaki etkinlik1'de şu şekilde yaptım:

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

Ve button2 olayında etkinlik2'de böyle yaptım:

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

Şimdi olacak olan şey, etkinlik2'de yaptığımız değişiklikler ne olursa olsun kaybolmayacak ve etkinlik2'yi daha önce bıraktığımız haliyle görebiliriz.

Bunun cevap olduğuna inanıyorum ve bu benim için iyi çalışıyor. Yanlışım varsa düzelt.


2
@ bagusflyer daha spesifik olmak ister misiniz ??? Yorumunuz yardımcı olmuyor ve hiç kimse size bu konuda yardımcı olamaz.
Stephen Tetreault

2
Aynı uygulama içinde iki etkinlik: Bu farklı bir duruma bir cevaptır. OP, uygulamadan ayrılmakla ilgilidir (örneğin, ana sayfa düğmesi veya farklı bir uygulamaya geçmek için başka araçlar).
ToolmakerSteve

44

onSaveInstanceState()geçici veriler için (geri yüklendi onCreate()/ onRestoreInstanceState()), onPause()kalıcı veriler için (geri yüklendi onResume()). Android teknik kaynaklarından:

Etkinlik durduruluyorsa onSaveInstanceState () Android tarafından çağrılır ve devam ettirilmeden önce öldürülebilir! Bu, Etkinlik yeniden başlatıldığında aynı duruma yeniden başlatılması için gereken herhangi bir durumu depolaması gerektiği anlamına gelir. Bu onCreate () yönteminin karşılığıdır ve aslında onCreate () öğesine iletilen saveInstanceState Bundle, onSaveInstanceState () yönteminde outState ile oluşturduğunuz Bundle ile aynıdır.

onPause () ve onResume () de ücretsiz yöntemlerdir. onPause () her zaman Faaliyet sona erdiğinde çağrılsa bile (örneğin bir finish () çağrısıyla). Mevcut notu veritabanına geri kaydetmek için bunu kullanacağız. İyi uygulama, bir onPause () sırasında da serbest bırakılabilecek kaynakları serbest bırakmak, pasif durumdayken daha az kaynak almaktır.


40

onSaveInstanceState()Etkinlik arka plana geçtiğinde gerçekten çağrılır.

Dokümanlardan alıntı: "Bu yöntem, bir faaliyet öldürülmeden önce çağrılır, böylece gelecekte bir süre sonra geri geldiğinde durumunu geri yükleyebilir." Kaynak


37

Isı yalıtım levhasının azaltılmasına yardımcı olmak için aşağıdakileri kullanıyorum interfaceve örnek durumunun kaydedilmesi classiçin bir okuma / yazma işlemi yapıyorum Bundle.


İlk olarak, örnek değişkenlerinize açıklama eklemek için kullanılacak bir arayüz oluşturun:

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 {

}

Ardından, değerleri pakete kaydetmek için yansımanın kullanılacağı bir sınıf oluşturun:

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

}

Örnek kullanım:

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

}

Not: Bu kod, MIT lisansı altında lisanslanan AndroidAutowire adlı bir kütüphane projesinden uyarlanmıştır .


34

Bu arada genel olarak artık kullanmıyorum

Bundle savedInstanceState & Co

Yaşam döngüsü çoğu aktivite için çok karmaşıktır ve gerekli değildir.

Ve Google kendini ifade ediyor, güvenilir bile değil.

Yolum, değişiklikleri hemen tercihlere kaydetmektir:

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

SharedPreferences bir şekilde Bundles gibi çalışır. Ve doğal olarak ve ilk başta bu tür değerlerin tercihlerden okunması gerekir.

Karmaşık veriler söz konusu olduğunda, tercihleri ​​kullanmak yerine SQLite kullanabilirsiniz.

Bu kavramı uygularken, etkinlik, aralarında yeniden başlatmalar içeren bir ilk açık veya arka yığın nedeniyle yeniden açılmış olmasına bakılmaksızın, son kaydedilen durumu kullanmaya devam eder.


31

Orijinal soruya doğrudan cevap vermek. saveInstancestate, Etkinliğiniz asla yeniden oluşturulmadığı için boş.

Etkinliğiniz yalnızca şu durumlarda bir eyalet paketiyle yeniden oluşturulacaktır:

  • Yeni bir etkinlik örneği oluşturulmasını gerektirebilecek yönlendirmeyi veya telefon dilini değiştirmek gibi yapılandırma değişiklikleri.
  • İşletim sistemi etkinliği yok ettikten sonra uygulamaya arka plandan dönersiniz.

Android, bellek baskısı altındayken veya uzun bir süre arka planda kaldıktan sonra arka plan etkinliklerini yok edecektir.

Merhaba dünya örneğinizi test ederken Faaliyetten ayrılmanın ve Etkinliğe dönmenin birkaç yolu vardır.

  • Geri düğmesine bastığınızda Etkinlik tamamlanır. Uygulamanın yeniden başlatılması yepyeni bir örnektir. Arka plandan hiç geri dönmüyorsunuz.
  • Ana sayfa düğmesine bastığınızda veya görev değiştiriciyi kullandığınızda Etkinlik arka plana geçer. OnCreate uygulamasına geri giderken yalnızca Faaliyetin yok edilmesi gerektiğinde çağrılacaktır.

Çoğu durumda, yalnızca eve basarsanız ve ardından uygulamayı tekrar başlatırsanız etkinliğin yeniden oluşturulması gerekmez. Bellekte zaten var, bu yüzden onCreate () çağrılmayacak.

Ayarlar -> Geliştirici Seçenekleri altında "Etkinlikleri tutma" adlı bir seçenek vardır. Etkinleştirildiğinde Android, etkinlikleri her zaman yok eder ve arka plandayken yeniden oluşturur. En kötü durum senaryosunu simüle ettiği için geliştirirken etkin bırakmak için harika bir seçenektir. (Faaliyetlerinizi her zaman geri dönüştüren düşük bellek cihazı).

Diğer cevaplar, devleti depolamanın doğru yollarını öğretmeleri açısından değerlidir, ancak kodunuzun neden beklediğiniz şekilde çalışmadığını gerçekten cevapladıklarını hissetmedim.


28

onSaveInstanceState(bundle)Ve onRestoreInstanceState(bundle)yöntemler ekranı (yön değişikliğini) döner sadece iken, veri kalıcılık için yararlıdır.
Onlar beri (uygulamalar arasında geçiş yaparken bile iyi değildir onSaveInstanceState()yöntemi denir ama onCreate(bundle)ve onRestoreInstanceState(bundle)yine çağrılmaz.
Daha kalıcılık kullanımı paylaşılan tercihleri için. Bu makaleyi okuyun


2
Sizin durumunuzda onCreateve onRestoreInstanceStateçağrılmıyorsunuz çünkü Activityuygulamaları değiştirdiğinizde hiç yok edilmediğinden, hiçbir şeyi geri yüklemenize gerek yoktur. Android onSaveInstanceState, Etkinliğin daha sonra yok edilmesi durumunda arama yapar (tüm cihaz yapılandırması değiştiği ve Etkinliğin sıfırdan yeniden oluşturulması gerektiğinden, ekranı döndürürken% 100 kesinlik ile olur).
Vicky Chijwani

20

Benim sorunum sadece uygulama ömrü boyunca (yani aynı uygulama içinde diğer alt faaliyetlerini başlatmak ve cihazı vb döndürme dahil tek bir yürütme) sebat gerekiyordu oldu. Yukarıdaki cevapların çeşitli kombinasyonlarını denedim, ancak her durumda istediğim şeyi alamadım. Sonunda benim için işe yarayan onCreate sırasında saveInstanceState'e bir referans almaktı:

mySavedInstanceState=savedInstanceState;

ve bunu gerektiğinde benim değişkenleri içeriğini elde etmek için, satırları boyunca kullanın:

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

Kullandığım onSaveInstanceStateve onRestoreInstanceStateyukarıda önerilen ancak ben (örn kullanarak ben de veya alternatif olarak değiştirdiğinde değişkeni kaydetmek için benim yöntemi kullanmak sanırım putBoolean)


19

Kabul edilen cevap doğru olmasına rağmen, Icepick adlı bir kitaplığı kullanarak Android'de Etkinlik durumunu kaydetmenin daha hızlı ve daha kolay bir yöntemi vardır . Icepick, durumu sizin için kaydetme ve geri yükleme işleminde kullanılan tüm kazan plakası koduyla ilgilenen bir ek açıklama işlemcisidir.

Icepick ile böyle bir şey yapmak:

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

Bunu yapmakla aynı şey:

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, durumunu a ile kaydeden herhangi bir nesne ile çalışacaktır Bundle.


16

Bir aktivite oluşturulduğunda, onCreate () yöntemi çağrılır.

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

savedInstanceState, Bundle sınıfının ilk kez null olan bir nesnesidir, ancak yeniden oluşturulduğunda değerler içerir. Etkinlik durumunu kaydetmek için onSaveInstanceState () yöntemini geçersiz kılmanız gerekir.

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

değerlerinizi outState.putString ("key", "Welcome Back") gibi "outState" Bundle nesnesine koyun ve super'i çağırarak kaydedin. Etkinlik yok edildiğinde durum Bundle nesnesine kaydedilir ve onCreate () veya onRestoreInstanceState () öğesinde yeniden oluşturulduktan sonra geri yüklenebilir. OnCreate () ve onRestoreInstanceState () öğelerinde alınan paket aynıdır.

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

veya

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

15

Bu değişikliği uygulamanın iki yolu vardır.

  1. kullanarak onSaveInstanceState()veonRestoreInstanceState() .
  2. Manifest olarak android:configChanges="orientation|screenSize".

Gerçekten ikinci yöntemi kullanmanızı önermiyorum. Deneyimlerimden birinde, portre ekranından manzaraya dönerken cihaz ekranının yarısının siyah olmasına neden oldu.

Yukarıda belirtilen ilk yöntemi kullanarak, yön değiştirildiğinde veya herhangi bir yapılandırma değişikliği olduğunda verileri saklayabiliriz. Kaydedilen durum durumu nesnesi içinde her türlü veri depolamak için bir yol biliyorum.

Örnek: Json nesnesini devam ettirmek istiyorsanız bir durumu düşünün. alıcılar ve ayarlayıcılarla bir model sınıfı oluşturun.

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

Şimdi onCreate ve onSaveInstanceState yöntemindeki etkinliğinizde aşağıdakileri yapın. Bunun gibi bir şey olacak:

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

}

11

İşte bir yorumdur Steve Moseley 'ın cevabı (tarafından ToolmakerSteve ) bu bakış içine koyar şeyler (OnPause vs bütün onSaveInstanceState içinde, batı maliyet destan vs doğu maliyet)

@VVK - Kısmen katılmıyorum. Bir uygulamadan çıkmanın bazı yolları onSaveInstanceState'i (oSIS) tetiklemez. Bu, oSIS'in kullanışlılığını sınırlar. Minimum işletim sistemi kaynakları için desteklemeye değer, ancak bir uygulama kullanıcıyı bulundukları duruma geri döndürmek istiyorsa, uygulamadan nasıl çıkılmış olursa olsun, bunun yerine kalıcı bir depolama yaklaşımı kullanmak gerekir. Paketi kontrol etmek için onCreate kullanıyorum ve eksikse kalıcı depolama alanını kontrol edin . Bu karar almayı merkezileştirir. Bir çökmeden kurtarabilir veya geri düğmesi çıkış veya özel menü öğesi Çıkış, ya da ekran kullanıcısına geri almak birçok gün sonra oldu. 19 Eylül - 22:38


10

Kotlin kodu:

kayıt etmek:

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

ve sonra onCreate()veyaonRestoreInstanceState()

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

Seçeneklere sahip olmak istemiyorsanız varsayılan değerleri ekleyin


9

Etkinlik durumu verilerini depolamak için onCreate(), önce geçersiz kılma SaveInstanceState(Bundle savedInstanceState)yöntemiyle verileri saveInstanceState öğesine kaydetmeniz gerekir .

Etkinlik yok etme SaveInstanceState(Bundle savedInstanceState)yöntemi çağrıldığında kaydetmek istediğiniz verileri kaydedersiniz. onCreate()Etkinlik yeniden başlatıldığında da aynı şeyi alırsınız .


6

Basit hızlı bu sorunu çözmek için kullanıyor IcePick

İlk olarak, kütüphaneyi app/build.gradle

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

Şimdi, Etkinlikte durumun nasıl kaydedileceğini aşağıda bu örneğe bakalım

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

Etkinlikler, Parçalar veya durumunu bir Paket üzerinde serileştirmesi gereken herhangi bir nesne (örn. Harcın ViewPresenters) için çalışır

Icepick ayrıca özel Görünümler için örnek durum kodu da oluşturabilir:

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
}

1
@ ralphspoon evet, Fragment ve Custom View için çalışır. Lütfen örnek kodu kontrol edin. Cevabımı düzenledim. Daha fazla kod örneği bulmak için github.com/frankiesardo/icepick resmi dokümanlarına gitmenizi öneririm .
THANN Phearum

@ChetanMehra özel görünüm sınıfı demek istiyorsun, değil mi? Özel görünümse, yukarıdaki CustomView örneğinde olduğu gibi onSaveInstanceState ve onRestoreInstanceState öğelerini geçersiz kılabiliriz.
THANN Phearum

Görünüm sınıfı içindeki sınıf nesnesini kastediyorum: sınıf CustomView genişletir Görünüm {@State Sınıf A a;} veya sınıf CustomView genişletir Görünüm {@ Durum İç sınıf {}}
Chetan Mehra

@THANNPhearum Başka bir soru olarak sormalı mıyım?
Chetan Mehra

Anlıyorum. Öyleyse, ClassA'nız Parcelable olmalıdır. Faaliyetler, Parçalar veya durumunu bir Paket üzerinde serileştirmesi gereken herhangi bir nesne için çalıştığı belirtildiği gibi
THANN Phearum

6

Çözümüm kaşlarını çattı olup olmadığından emin değilim, ancak ViewModel durumunu devam ettirmek için bağlı bir hizmet kullanıyorum. Hizmette bellekte saklayıp saklamadığınız veya bir SQLite veritabanından alıp almadığınız gereksinimlerinize bağlıdır. Herhangi bir lezzetin hizmetleri budur, uygulama durumunu korumak ve soyut ortak iş mantığı gibi hizmetler sağlarlar.

Mobil cihazlarda bulunan bellek ve işlem kısıtlamaları nedeniyle, Android görünümlerini bir web sayfasına benzer şekilde ele alıyorum. Sayfa durumu korumaz, yalnızca amacı uygulama durumunu sunmak ve kullanıcı girişini kabul etmek olan bir sunum katmanı bileşenidir. Web uygulaması mimarisindeki son eğilimler, sayfanın Görünüm, etki alanı verileri model olduğu ve denetleyici bir web hizmetinin arkasında yer alan, eski Model, Görünüm, Denetleyici (MVC) desenini kullanır. Aynı desen, Görünüm, Android ... ile kullanılabilir, Görünüm, model etki alanı verilerinizdir ve Denetleyici, Android bağlantılı bir hizmet olarak uygulanır. Bir görünümün denetleyici ile etkileşime girmesini istediğinizde, başlatma / devam ettirme sırasında ona bağlanma ve durdurma / duraklatmada bağlama.

Bu yaklaşım, tüm uygulama iş mantığınızın birden çok görünümde çoğaltılan mantığı azaltan ve görünümün başka bir önemli tasarım prensibi olan Tek Sorumluluk'u uygulayabilmesi için Endişe Ayırma tasarım ilkesini uygulamanıza ek bonus sağlar.


5

Kotlin

Geçersiz kılmalı onSaveInstanceStateveonRestoreInstanceStateKalıcı olmasını istediğiniz değişkenlerinizi depolamanız ve

Yaşam döngüsü grafiği

Mağaza değişkenleri

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

Değişkenleri al

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
}

2

Android, durumu kaydetmek için ViewModels sağlıyor, saveInstanceState yerine bunu kullanmaya çalışmalısınız.


3
Bu doğru değil. Belgelerden: "Kaydedilen örnek durumundan farklı olarak, ViewModels sistem tarafından başlatılan bir işlem ölümü sırasında imha edilir. Bu nedenle ViewModel nesnelerini onSaveInstanceState () (veya başka bir disk kalıcılığı) ile birlikte kullanmanız gerekir. modelleri sistem ölümünden sonra verileri yeniden yükler. "
Vyacheslav Martynenko

Sadece arka planda değişen izinler ile bu koştu.
Brill Pappin

"Sistem tarafından başlatılan işlem ölümünü işlemeniz gerekiyorsa, onSaveInstanceState () öğesini yedek olarak kullanmak isteyebilirsiniz" belgesinden katılıyorum.
Zhar

2

Herhangi bir yöntem uygulamadan Android'in durumları kaydetmesini sağlamanın bir yolu var. Bu satırı Etkinlik bildirimindeki Bildiriminize ekleyin:

android:configChanges="orientation|screenSize"

Şöyle görünmelidir:

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

Burada bu mülk hakkında daha fazla bilgi bulabilirsiniz.

Android'in bunu sizin için elle işlemden daha iyi yapması önerilir.


2
Bunun durumu kaydetmeyle ilgisi yok, sadece yön değişikliklerini bırakıyorsunuz, uygulamanın farklı etkinlikler için herhangi bir zamanda yeniden başlatılabileceğini ve duraklatılabileceğini ve devam ettirilebileceğini unutmayın
lord-ralf-adolf

1
Bu cevap, yönelim değiştiğinde durumu kurtarmak ve karmaşık bir yolun anlaşılmasını ve uygulanmasını önlemek
isteyenler içindir

Dürüst olduğunuzu görüyorum, devleti kurtarmak için mücadele eden çoğu insan parça kullanıyor çünkü aktiviteler bir ID'ye sahip oldukları sürece aslında UI bileşenlerinin statüsünü kaydediyor, ancak parçalar daha özel, parçaları bir kez kullandım ama asla kullanmam Onları tekrar kurtarmak örnek stat başa çıkmak için bir acı oldu
lord-ralf-adolf

çalışıyor ... teşekkürler
Fanadez

1

Ne kurtarmalı ve ne yapılmamalı?

Hiç merak ettiniz mi? EditText yön değiştirilirken otomatik olarak kaydedildiğini mi? Bu cevap tam size göre.

Bir Etkinliğin bir örneği yok edildiğinde ve Sistem yeni bir örneği yeniden oluşturduğunda (örneğin, yapılandırma değişikliği). Eski Etkinlik Durumunun ( örnek durum ) kaydedilmiş bir veri kümesini kullanarak yeniden oluşturmaya çalışır .

Eşgörünüm durumu, bir nesnede depolanan anahtar / değer çiftlerinin bir koleksiyonudur Bundle.

Varsayılan olarak Sistem örneğin Nesneleri Paket'e kaydeder.

  • İçindeki metin EditText
  • A ListViewvb. Konumunda kaydırma konumu .

Örnek durumunun bir parçası olarak kaydedilmek için başka bir değişkene ihtiyacınız varsa OVERRIDE onSavedInstanceState(Bundle savedinstaneState) yöntemini kullanmalısınız.

Örneğin, int currentScorebir GameActivity'de

Verileri kaydederken onSavedInstanceState (Bundle savedinstaneState) hakkında daha fazla ayrıntı

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

Bu nedenle yanlışlıkla super.onSaveInstanceState(savedInstanceState);varsayılan davranışı çağırmayı unutursanız, yani EditText'teki Metin kaydedilmez.

Etkinlik durumunu geri yüklemek için hangisi seçilir?

 onCreate(Bundle savedInstanceState)

VEYA

onRestoreInstanceState(Bundle savedInstanceState)

Her iki yöntem de aynı Bundle nesnesini alır, bu nedenle geri yükleme mantığınızı nereye yazdığınız önemli değildir. Tek fark, onCreate(Bundle savedInstanceState)yöntemde, ikinci durumda gerekli olmadığında boş bir denetim vermek zorunda kalacağınızdır. Diğer yanıtlarda zaten kod parçacıkları var. Onlara başvurabilirsiniz.

OnRestoreInstanceState (Bundle savedinstaneState) hakkında daha fazla ayrıntı

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

super.onRestoreInstanceState(savedInstanceState);Sistem varsayılan olarak Görünüm hiyerarşisini geri yükleyecek şekilde çağırın

Bonus

onSaveInstanceState(Bundle savedInstanceState)Kullanıcı Faaliyet geri gelmek niyetinde yalnızca sistem tarafından çağrılır. Örneğin, App X kullanıyorsunuz ve aniden bir çağrı alıyorsunuz. Arayan uygulamasına geçip X uygulamasına geri dönersiniz. Bu durumda onSaveInstanceState(Bundle savedInstanceState)yöntem çağrılır.

Ancak bir kullanıcı geri düğmesine basarsa bunu göz önünde bulundurun. Kullanıcının Aktiviteye geri dönmeyi amaçlamadığı varsayılır, dolayısıyla bu durumda onSaveInstanceState(Bundle savedInstanceState)sistem tarafından çağrılmaz. Verileri kaydederken tüm senaryoları göz önünde bulundurmanız gerektiğini unutmayın.

İlgili bağlantılar:

Demo varsayılan davranış
Android Resmi Belgeler .


1

Şimdi görünüm modelinde 2 yol yapmak mantıklı. ilkini kaydedilmiş bir örnek olarak kaydetmek istiyorsanız: Görünüm modeline durum parametresini şu şekilde ekleyebilirsiniz https://developer.android.com/topic/libraries/architecture/viewmodel-savedstate#java

veya değişkenleri veya nesneyi görünüm modeline kaydedebilirsiniz, bu durumda görünüm modeli etkinlik yok olana kadar yaşam döngüsünü tutacaktır.

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

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

public class HelloAndroid extends Activity {

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

    mTextView = new TextView(this);

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

    setContentView(mTextView);
  }
}

haklısın, ama bu kütüphane hala serbest bırakıldı, bu yüzden beklememiz gerektiğini düşünüyorum ...
Zhar

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.