सहेजें इंस्टेंस स्टेट का उपयोग करके Android गतिविधि स्थिति सहेजना



11 Answers

सहेजे गए इंस्टेंसस्टेट केवल गतिविधि के वर्तमान उदाहरण से जुड़े राज्य को सहेजने के लिए है, उदाहरण के लिए वर्तमान नेविगेशन या चयन जानकारी, ताकि अगर एंड्रॉइड किसी गतिविधि को नष्ट कर देता है और पुन: प्रयास करता है, तो यह पहले जैसा था, उतना वापस आ सकता है। onSaveInstanceState और onSaveInstanceState लिए प्रलेखन देखें

अधिक लंबे समय तक रहने वाले राज्य के लिए, SQLite डेटाबेस, फ़ाइल या प्राथमिकताओं का उपयोग करने पर विचार करें। सेविंग पर्सिस्टेंट स्टेट देखें।

Question

मैं एंड्रॉइड एसडीके मंच पर काम कर रहा हूं, और यह थोड़ा अस्पष्ट है कि एप्लिकेशन के राज्य को कैसे सहेजना है। इसलिए 'हैलो, एंड्रॉइड' उदाहरण के इस मामूली पुन: टूलिंग को दिया गया:

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

मैंने सोचा कि यह सबसे आसान मामले के लिए पर्याप्त होगा, लेकिन यह हमेशा पहले संदेश के साथ प्रतिक्रिया करता है, इससे कोई फर्क नहीं पड़ता कि मैं ऐप से कैसे नेविगेट करता हूं।

मुझे यकीन है कि समाधान ओवरराइडिंग के समान सरल है या ऐसा कुछ है, लेकिन मैं 30 मिनट या उससे अधिक के लिए प्रलेखन में दूर हो रहा हूं और कुछ भी स्पष्ट नहीं मिला है।




When an activity is created it's onCreate() method is called.

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

savedInstanceState is an object of Bundle class which is null for the first time, but it contains values when it is recreated. To save Activity's state you have to override onSaveInstanceState().

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

put your values in "outState" Bundle object like outState.putString("key","Welcome Back") and save by calling super. When activity will be destroyed it's state get saved in Bundle object and can be restored after recreation in onCreate() or onRestoreInstanceState(). Bundle received in onCreate() and onRestoreInstanceState() are same.

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

या

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



To get activity state data stored in onCreate() , first you have to save data in savedInstanceState by overriding SaveInstanceState(Bundle savedInstanceState) method.

जब गतिविधि नष्ट SaveInstanceState(Bundle savedInstanceState)हो जाती है विधि को बुलाया जाता है और वहां आप उस डेटा को सहेजते हैं जिसे आप सहेजना चाहते हैं। और onCreate()गतिविधि में पुनरारंभ होने पर आप वही प्राप्त होते हैं । (सहेजे गए इंस्टेंसस्टेट शून्य नहीं होगा क्योंकि गतिविधि नष्ट होने से पहले आपने इसमें कुछ डेटा सहेजा है)




क्षणिक डेटा के लिए onRestoreInstanceState() लगातार डेटा ( onRestoreInstanceState() onCreate() / onRestoreInstanceState() पुनर्स्थापित, निरंतर डेटा के लिए ( onResume() ) onResume() में पुनर्स्थापित)। एंड्रॉइड तकनीकी संसाधनों से:

ऑनसेवस्टेंसस्टेट () को एंड्रॉइड द्वारा बुलाया जाता है यदि गतिविधि बंद हो रही है और इसे फिर से शुरू होने से पहले मार दिया जा सकता है! इसका अर्थ यह है कि गतिविधि को पुनरारंभ करते समय उसी स्थिति में फिर से शुरू करने के लिए आवश्यक किसी भी राज्य को स्टोर करना चाहिए। यह ऑनक्रेट () विधि का समकक्ष है, और वास्तव में सहेजे गए इन्स्टेंसस्टेट बंडल को क्रिएट () में पास किया गया वही बंडल है जिसे आप ऑनवेवस्टेंसस्टेट () विधि में आउटस्टेट के रूप में बनाते हैं।

onPause () और onResume () भी मानार्थ विधियां हैं। ऑन पॉज़ () को हमेशा गतिविधि के समाप्त होने पर बुलाया जाता है, भले ही हम इसे उत्तेजित करते हैं (उदाहरण के लिए एक फिनिश () कॉल के साथ)। हम वर्तमान नोट को वापस डेटाबेस में सहेजने के लिए इसका उपयोग करेंगे। अच्छा अभ्यास किसी भी संसाधन को जारी करना है जिसे निष्क्रिय स्थिति में कम संसाधनों को लेने के लिए ऑनऑज़ () के दौरान भी जारी किया जा सकता है।




Really onSaveInstance state callen when the Activity goes to background

Quote from the docs: "the method onSaveInstanceState(Bundle) is called before placing the activity in such a background state"




The onSaveInstanceState(bundle) and onRestoreInstanceState(bundle) methods are useful for data persistence merely while rotating the screen (orientation change).
They are not even good while switching between applications (since the onSaveInstanceState() method is called but onCreate(bundle) and onRestoreInstanceState(bundle) is not invoked again.
For more persistence use shared preferences. read this article




मेरे सहयोगी ने एंड्रॉइड उपकरणों पर एप्लिकेशन स्टेट को समझाते हुए एक लेख लिखा जिसमें गतिविधि लाइफसाइक्ल और राज्य सूचना, राज्य जानकारी कैसे स्टोर करें, और राज्य Bundle और SharedPreferences और यहां पर एक नज़र SharedPreferences सहित स्पष्टीकरण शामिल हैं

लेख में तीन दृष्टिकोण शामिल हैं:

इंस्टेंस स्टेट बंडल का उपयोग करके एप्लिकेशन जीवनकाल (यानी अस्थायी रूप से) के लिए स्थानीय वैरिएबल / यूआई नियंत्रण डेटा स्टोर करें

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

साझा प्राथमिकताओं का उपयोग कर अनुप्रयोग उदाहरणों (यानी स्थायी रूप से) के बीच स्थानीय वैरिएबल / यूआई नियंत्रण डेटा स्टोर करें

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

बनाए गए गैर-कॉन्फ़िगरेशन इंस्टेंस का उपयोग करके एप्लिकेशन जीवनकाल में गतिविधियों के बीच स्मृति में ऑब्जेक्ट उदाहरणों को जीवित रखना

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



Although the accepted answer is correct, there is a faster and easier method to save the Activity state on Android using a library called Icepick . Icepick is an annotation processor that takes care of all the boilerplate code used in saving and restoring state for you.

Doing something like this with 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 the same as doing this:

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 will work with any object that saves its state with a Bundle .




Not sure if my solution is frowned upon or not but I use a bound service to persist ViewModel state. Whether you store it in memory in the service or persist and retrieve from a SqlLite database depends on your requirements. This is what services of any flavor do, they provide services such as maintaining application state and abstract common business logic.

Because of memory and processing constraints inherent on mobile devices, I treat Android views in a similar way to a web page. The page does not maintain state, it is purely a presentation layer component whose only purpose is to present application state and accept user input. Recent trends in web app architecture employ the use of the age old Model, View, Controller (MVC) pattern, where the page is the View, Domain data is the model and the controller sits behind a web service. The same pattern can be employed in android with the View being well ... the View, the model is your domain data and the Controller is implemented as an Android bound service. Whenever you want a view to interact with the controller, bind to it on start/resume and unbind on stop/pause.

This approach gives you the added bonus of enforcing the Separation of Concern design principle in that all of you application business logic can be moved into your service which reduces duplicated logic across multiple views and allows the view to enforce another important design principle, Single Responsibility.




जहां तक ​​मेरा संबंध है, राज्य को सहेजना सबसे अच्छा है। यदि आपको लगातार डेटा को सहेजने की आवश्यकता है, तो बस एक SQLite डेटाबेस का उपयोग करें। एंड्रॉइड इसे सूओ आसान बनाता है।

कुछ इस तरह:

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

उसके बाद एक साधारण कॉल

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



onSaveInstanceState को तब कहा जाता है जब सिस्टम को स्मृति की आवश्यकता होती है और एप्लिकेशन को मार देता है। यह तब नहीं कहा जाता जब उपयोगकर्ता सिर्फ एप्लिकेशन को बंद कर देता है। तो मुझे लगता है कि आवेदन स्थिति को भी सहेजना चाहिए क्योंकि इसे कुछ निरंतर भंडारण जैसे Preferences या Sqlite सहेजा जाना चाहिए




To help reduce boilerplate I use the following interface and class to read/write to a Bundle for saving instance state.

First, create an interface that will be used to annotate your instance variables:

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 {

}

Then, create a class where reflection will be used to save values to the bundle:

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

}

उदाहरण का उपयोग:

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

}

Note: This code was adapted from a library project named AndroidAutowire which is licensed under the MIT license .




Related