android - एडम - स्टेट णिच इन २०१८ नोटिफिकेशन




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

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

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 मिनट या उससे अधिक के लिए प्रलेखन में दूर हो रहा हूं और कुछ भी स्पष्ट नहीं मिला है।


अपनी परियोजना में लाइवडाटा (एंड्रॉइड आर्किटेक्चर घटक) जोड़ना

निम्नलिखित निर्भरता जोड़ें

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

LiveData एक पर्यवेक्षक में ले जाता है और इसे प्रारंभ होने या संशोधित स्थिति में होने पर डेटा परिवर्तनों के बारे में सूचित करता है। LiveData साथ लाभ यह है कि अपनी गतिविधि किसी भी अन्य की तुलना में राज्य में चला जाता है जब शुरू या फिर से शुरू यह फोन नहीं होगा onChanged पर विधि पर्यवेक्षक

private TextView mTextView;
private MutableLiveData<String> mMutableLiveData;

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

}

आपको onSaveInstanceState(Bundle savedInstanceState) पर ओवरराइड करने की आवश्यकता है और उस एप्लिकेशन onSaveInstanceState(Bundle savedInstanceState) को लिखना है जिसे आप 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.
}

बंडल अनिवार्य रूप से एक एनवीपी ("नाम-मूल्य जोड़ी") मानचित्र को संग्रहीत करने का एक तरीका है, और इसे onRestoreInstanceState() और onRestoreInstanceState() भी पारित किया जाएगा जहां आप इस तरह के मान onRestoreInstanceState() :

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

आप आमतौर पर इस तकनीक का उपयोग अपने आवेदन (चयन, सहेजे गए पाठ इत्यादि) के उदाहरण मानों को संग्रहीत करने के लिए करेंगे।


दोनों विधियां उपयोगी और वैध हैं और दोनों अलग-अलग परिदृश्यों के लिए सबसे उपयुक्त हैं:

  1. उपयोगकर्ता एप्लिकेशन को समाप्त कर देता है और इसे बाद की तारीख में फिर से खोलता है, लेकिन एप्लिकेशन को अंतिम सत्र से डेटा पुनः लोड करने की आवश्यकता होती है - इसके लिए SQLite का उपयोग करने जैसे लगातार संग्रहण दृष्टिकोण की आवश्यकता होती है।
  2. उपयोगकर्ता एप्लिकेशन को स्विच करता है और फिर मूल पर वापस आ जाता है और जहां से वे छोड़ा जाता है उसे चुनना चाहता है - onSaveInstanceState() और onRestoreInstanceState() पर बंडल डेटा (जैसे एप्लिकेशन स्टेट डेटा) को सहेजना और पुनर्स्थापित करना आमतौर पर पर्याप्त है।

यदि आप राज्य डेटा को लगातार तरीके से सहेजते हैं, तो इसे onResume() या onResume() onCreate() (या वास्तव में किसी भी जीवन चक्र कॉल पर) में पुनः लोड किया जा सकता है। यह वांछित व्यवहार हो सकता है या नहीं भी हो सकता है। यदि आप इसे InstanceState में बंडल में संग्रहीत करते हैं, तो यह क्षणिक होता है और केवल उसी उपयोगकर्ता के सत्र में उपयोग के लिए डेटा संग्रहीत करने के लिए उपयुक्त होता है (मैं शब्द सत्र को कम से कम उपयोग करता हूं) लेकिन 'सत्र' के बीच नहीं।

ऐसा नहीं है कि एक दृष्टिकोण दूसरे की तुलना में बेहतर है, सबकुछ की तरह, यह समझना महत्वपूर्ण है कि आपको किस व्यवहार की आवश्यकता है और सबसे उपयुक्त दृष्टिकोण चुनना है।


ध्यान दें कि developer.android.com/reference/android/app/Activity.html में गतिविधि राज्यों पर दस्तावेज़ों के अनुसार, लगातार डेटा के लिए onSaveInstanceState और onRestoreInstanceState पर उपयोग करना सुरक्षित नहीं है

दस्तावेज़ बताता है ('गतिविधि लाइफसाइक्ल' अनुभाग में):

ध्यान दें कि लगातार डेटा को onSaveInstanceState(Bundle) onPause() बजाय onSaveInstanceState(Bundle) onPause() में सहेजना महत्वपूर्ण है क्योंकि बाद में लाइफसाइक्ल कॉलबैक का हिस्सा नहीं है, इसलिए इसके दस्तावेज़ में वर्णित हर स्थिति में नहीं कहा जाएगा।

दूसरे शब्दों में, onResume() और onResume() लगातार डेटा के लिए अपना सेव / पुनर्स्थापित कोड onResume() !

संपादित करें : आगे स्पष्टीकरण के लिए, यहां पर onSaveInstanceState() दस्तावेज़ है:

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


मेरे सहयोगी ने एंड्रॉइड उपकरणों पर एप्लिकेशन स्टेट को समझाते हुए एक लेख लिखा जिसमें गतिविधि लाइफसाइक्ल और राज्य सूचना, राज्य जानकारी कैसे स्टोर करें, और राज्य 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();
}

यह एंड्रॉइड विकास का क्लासिक 'गॉचा' है। यहां दो समस्याएं हैं:

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

इन सभी धागे में ब्राउज़िंग, मुझे संदेह है कि ज्यादातर समय डेवलपर्स इन दो अलग-अलग मुद्दों के बारे में बात कर रहे हैं ... इसलिए सभी भ्रम और रिपोर्ट "यह मेरे लिए काम नहीं करती है"।

सबसे पहले, 'इच्छित' व्यवहार को स्पष्ट करने के लिए: ऑनसेवेंस और ऑनस्टोर इंस्टेंस नाजुक हैं और केवल क्षणिक स्थिति के लिए हैं। इच्छित उपयोग (afaict) फोन घुमाए जाने पर गतिविधि मनोरंजन को संभालना है (अभिविन्यास परिवर्तन)। दूसरे शब्दों में, इच्छित उपयोग तब होता है जब आपकी गतिविधि अभी भी 'शीर्ष पर' तर्कसंगत है, लेकिन फिर भी सिस्टम द्वारा इसे पुनर्स्थापित किया जाना चाहिए। सहेजे गए बंडल को प्रक्रिया / मेमोरी / जीसी के बाहर नहीं रखा जाता है, इसलिए यदि आप पृष्ठभूमि में जाते हैं तो आप वास्तव में इस पर भरोसा नहीं कर सकते हैं। हां, शायद आपकी गतिविधि की याददाश्त पृष्ठभूमि की अपनी यात्रा से बच जाएगी और जीसी से बच जाएगी, लेकिन यह विश्वसनीय नहीं है (न ही यह अनुमानित है)।

तो यदि आपके पास ऐसा परिदृश्य है जहां सार्थक 'उपयोगकर्ता प्रगति' या राज्य है जो आपके आवेदन के 'लॉन्च' के बीच जारी रहना चाहिए, तो मार्गदर्शन और कारण पर उपयोग करना है। आपको खुद को एक सतत स्टोर चुनना और तैयार करना होगा।

लेकिन - एक बहुत भ्रमित बग है जो इस सब को जटिल बनाता है। विवरण यहां हैं:

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

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

असल में, यदि आपका एप्लिकेशन सिंगलटास्क ध्वज के साथ लॉन्च किया गया है, और उसके बाद आप इसे होम स्क्रीन या लॉन्चर मेनू से लॉन्च करते हैं, तो उसके बाद के आमंत्रण से एक नया कार्य तैयार होगा ... आपके प्रभावी ढंग से आपके ऐप के दो अलग-अलग उदाहरण होंगे एक ही ढेर में रहना ... जो बहुत अजीब हो जाता है। ऐसा तब होता है जब आप विकास के दौरान अपना ऐप लॉन्च करते हैं (यानी एक्लिप्स या इंटेलिज से), इसलिए डेवलपर्स इसमें बहुत कुछ चलाते हैं। लेकिन कुछ ऐप स्टोर अपडेट मैकेनिज्म के माध्यम से भी (इसलिए यह आपके उपयोगकर्ताओं को भी प्रभावित करता है)।

मैंने इन थ्रेडों के माध्यम से घंटों तक लड़ाई लड़ी थी इससे पहले कि मुझे एहसास हुआ कि मेरा मुख्य मुद्दा यह बग था, इरादा ढांचा व्यवहार नहीं। एक महान लेखन और वैकल्पिक हल (अद्यतन: नीचे देखें) इस उत्तर में उपयोगकर्ता @kaciula से प्रतीत होता है:

होम कुंजी प्रेस व्यवहार

जून 2013 अपडेट करें : महीनों बाद, मुझे अंत में 'सही' समाधान मिला है। आपको किसी भी राज्य के शुरूआती ऐप को स्वयं प्रबंधित करने की आवश्यकता नहीं है, आप इसे ढांचे से और उचित रूप से जमानत से पहचान सकते हैं। मैं इसे अपने 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()लगातार डेटा (पुनर्स्थापित ) के लिए क्षणिक डेटा ( onCreate()/ में बहाल / onRestoreInstanceState()) के onPause()लिए onResume()। एंड्रॉइड तकनीकी संसाधनों से:

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

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


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


इस परिवर्तन को लागू करने के मूल रूप से दो तरीके हैं।

  1. का उपयोग कर onSaveInstanceState()और onRestoreInstanceState()
  2. प्रकट में android:configChanges="orientation|screenSize"

मैं वास्तव में दूसरी विधि का उपयोग करने की सिफारिश नहीं करता हूं। चूंकि मेरे अनुभव में से एक में पोर्ट्रेट से परिदृश्य और इसके विपरीत घूर्णन करते समय डिवाइस स्क्रीन का आधा हिस्सा काला हो रहा था।

उपरोक्त वर्णित पहली विधि का उपयोग करते समय, जब अभिविन्यास बदल जाता है या कोई कॉन्फ़िगरेशन परिवर्तन होता है तो हम डेटा को जारी रख सकते हैं। मुझे पता है कि आप सहेजे गए इन्स्टेंस स्टेट ऑब्जेक्ट के अंदर किसी भी प्रकार का डेटा स्टोर कर सकते हैं।

उदाहरण: यदि आप जेसन ऑब्जेक्ट को जारी रखना चाहते हैं तो एक मामले पर विचार करें। गेटर्स और सेटर्स के साथ एक मॉडल क्लास बनाएं।

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

अब क्रिएट और ऑनसेवस्टेंसस्टेट विधि में आपकी गतिविधि में निम्नलिखित कार्य करें। यह ऐसा कुछ दिखाई देगा:

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

}

इस बीच मैं सामान्य रूप से और अधिक उपयोग नहीं करता हूं

Bundle savedInstanceState & Co

लाइव चक्र ज्यादातर गतिविधियों के लिए बहुत जटिल है और आवश्यक नहीं है। और Google खुद ही कहता है, यह भी भरोसेमंद नहीं है।

मेरा तरीका वरीयताओं में तत्काल किसी भी बदलाव को सहेजना है

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

कुछ तरीकों से साझा किए गए संदर्भ बंडलों की तरह काम करते हैं। और स्वाभाविक रूप से और पहले ऐसे मूल्यों को वरीयताओं से लाल होना चाहिए।

जटिल डेटा के मामले में आप वरीयताओं का उपयोग करने के बजाय स्क्लाइट का उपयोग कर सकते हैं।

इस अवधारणा को लागू करते समय, गतिविधि अभी भी अंतिम सहेजी गई स्थिति का उपयोग जारी रखती है, भले ही यह रीबूट के साथ प्रारंभिक खुला था या बैक स्टैक के कारण फिर से खोलना था।


जब कोई गतिविधि बनाई जाती है तो यह क्रिएट () विधि को कॉल किया जाता है।

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

savedInstanceState बंडल क्लास का एक ऑब्जेक्ट है जो पहली बार शून्य है, लेकिन इसमें इसे पुनर्निर्मित होने पर मान शामिल हैं। गतिविधि की स्थिति को सहेजने के लिए आपको SaveInstanceState () पर ओवरराइड करना होगा।

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

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

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

onSaveInstanceजब गतिविधि पृष्ठभूमि में जाती है तो वास्तव में राज्य कॉलन

दस्तावेज़ों से उद्धरण: "इस onSaveInstanceState(Bundle)तरह की पृष्ठभूमि स्थिति में गतिविधि रखने से पहले विधि को बुलाया जाता है"


अब एंड्रॉइड राज्य को सहेजने के लिए ViewModels प्रदान करता है , आपको इसेInstanceState को सहेजने के बजाय उपयोग करने का प्रयास करना चाहिए।


इस समस्या को हल करने के लिए सरल त्वरित Icepick का उपयोग कर Icepick

सबसे पहले, पुस्तकालय में सेटअप करें app/build.gradle

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

अब, गतिविधि में राज्य को कैसे सहेजना है, इस उदाहरण को नीचे देखें

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

यह क्रियाकलापों, टुकड़ों या किसी ऑब्जेक्ट के लिए काम करता है जिसे बंडल पर अपने राज्य को क्रमबद्ध करने की आवश्यकता होती है (उदाहरण के लिए मोर्टार के व्यूपेंटर)

आइसपिक कस्टम दृश्यों के लिए इंस्टेंस स्टेट कोड भी उत्पन्न कर सकता है:

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
}

बॉयलरप्लेट को कम करने में मदद के लिए मैं निम्नलिखित का उपयोग करता हूं interfaceऔर उदाहरण स्थिति को सहेजने के लिए classपढ़ने / लिखने के लिए Bundle

सबसे पहले, एक इंटरफ़ेस बनाएं जिसका उपयोग आपके इंस्टेंस चर को एनोटेट करने के लिए किया जाएगा:

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 {

}

फिर, एक वर्ग बनाएं जहां प्रतिबिंब का उपयोग बंडल को मूल्यों को सहेजने के लिए किया जाएगा:

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

}

नोट: यह कोड AndroidAutowire नामक लाइब्रेरी प्रोजेक्ट से अनुकूलित किया गया था जिसे एमआईटी लाइसेंस के तहत लाइसेंस प्राप्त है ।


मेरी समस्या यह थी कि मुझे केवल आवेदन जीवनकाल के दौरान दृढ़ता की आवश्यकता होती है (यानी एक ही निष्पादन जिसमें एक ही ऐप के भीतर अन्य उप-गतिविधियां शुरू करना और डिवाइस को घूर्णन करना आदि शामिल हैं)। मैंने उपर्युक्त उत्तरों के विभिन्न संयोजनों की कोशिश की लेकिन मुझे सभी परिस्थितियों में जो चाहिए था वह नहीं मिला। अंत में मेरे लिए क्या काम किया गया था सहेजने के दौरान सहेजे गए इंस्टेंसस्टेट का संदर्भ प्राप्त करना:

mySavedInstanceState=savedInstanceState;

और जब मैं इसकी आवश्यकता होती है, तो मेरी चर की सामग्री प्राप्त करने के लिए इसका उपयोग करें:

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

मैं उपरोक्त सुझाव देता हूं onSaveInstanceStateऔर onRestoreInstanceStateसुझाव देता हूं लेकिन मुझे लगता है कि जब मैं बदलता हूं तो वैरिएबल को सहेजने के लिए मैं अपनी विधि का वैकल्पिक रूप से उपयोग कर सकता हूं (उदाहरण के लिए putBoolean)


यहाँ से एक टिप्पणी है स्टीव मोसली के जवाब (द्वारा ToolmakerSteve ) उस परिप्रेक्ष्य में चीजों को कहते हैं (पूरे onSaveInstanceState बनाम onPause में, पूर्व लागत बनाम पश्चिम लागत गाथा)

@ वीवीके - मैं आंशिक रूप से असहमत हूं। ऐप से बाहर निकलने के कुछ तरीके SaveInstanceState (oSIS) पर ट्रिगर नहीं करते हैं। यह ओएसआईएस की उपयोगिता को सीमित करता है। कम से कम ओएस संसाधनों के लिए इसका समर्थन करने लायक है, लेकिन यदि कोई ऐप उपयोगकर्ता को राज्य में वापस लौटना चाहता है, तो इससे कोई फर्क नहीं पड़ता कि ऐप कैसे निकला था, इसके बजाय लगातार स्टोरेज दृष्टिकोण का उपयोग करना आवश्यक है। मैं बंडल की जांच करने के लिए क्रेट का उपयोग करता हूं, और यदि यह गुम है, तो लगातार भंडारण की जांच करें यह निर्णय लेने को केंद्रीकृत करता है। मैं एक दुर्घटना, या बैक बटन निकास या कस्टम मेनू आइटम से बाहर निकल सकता हूं, या कई दिनों बाद स्क्रीन उपयोगकर्ता पर वापस आ गया। - टूलमेकरस्टेव सितम्बर 1 9 '15 10:38 बजे


सीधे मूल प्रश्न का उत्तर देने के लिए। सहेजा गया इंस्टेंटस्टेट शून्य है क्योंकि आपकी गतिविधि को फिर से बनाया नहीं जा रहा है।

आपकी गतिविधि केवल एक राज्य बंडल के साथ फिर से बनाई जाएगी जब:

  • कॉन्फ़िगरेशन परिवर्तन जैसे अभिविन्यास या फ़ोन भाषा को बदलना, जिसके लिए एक नया गतिविधि उदाहरण बनाया जाना आवश्यक हो सकता है।
  • ओएस ने गतिविधि को नष्ट करने के बाद आप पृष्ठभूमि से ऐप पर वापस आ गए हैं।

एंड्रॉइड मेमोरी प्रेशर के तहत या विस्तारित अवधि के लिए पृष्ठभूमि में होने के बाद पृष्ठभूमि गतिविधियों को नष्ट कर देगा।

अपने हैलो वर्ल्ड उदाहरण का परीक्षण करते समय छोड़ने और गतिविधि पर वापस आने के कुछ तरीके हैं।

  • जब आप बैक बटन दबाते हैं तो गतिविधि समाप्त हो जाती है। ऐप को फिर से लॉन्च करना एक नया नया उदाहरण है। आप पृष्ठभूमि से फिर से शुरू नहीं हो रहे हैं।
  • जब आप होम बटन दबाते हैं या टास्क स्विचर का उपयोग करते हैं तो गतिविधि पृष्ठभूमि में जाएगी। जब आवेदन पर वापस नेविगेट किया जाता है तो क्रिएट को केवल तभी बुलाया जाएगा जब गतिविधि को नष्ट किया जाना था।

ज्यादातर मामलों में यदि आप केवल घर दबा रहे हैं और फिर ऐप लॉन्च कर रहे हैं तो गतिविधि को फिर से बनाया जाने की आवश्यकता नहीं होगी। यह स्मृति में पहले से मौजूद है इसलिए क्रिएट () को कॉल नहीं किया जाएगा।

सेटिंग -> डेवलपर विकल्प के तहत एक विकल्प है जिसे "गतिविधियां न रखें" कहा जाता है। जब यह सक्षम हो जाता है एंड्रॉइड हमेशा गतिविधियों को नष्ट कर देगा और पृष्ठभूमि के दौरान उन्हें फिर से बनाएगा। विकास के दौरान सक्षम होने के लिए यह एक शानदार विकल्प है क्योंकि यह सबसे खराब स्थिति परिदृश्य को अनुकरण करता है। (एक कम स्मृति उपकरण हर समय आपकी गतिविधियों को रीसाइक्लिंग)।

अन्य उत्तरों मूल्यवान हैं कि वे आपको राज्य को स्टोर करने के सही तरीके सिखाते हैं लेकिन मुझे नहीं लगता कि उन्होंने वास्तव में जवाब दिया है कि आपका कोड जिस तरह से अपेक्षित तरीके से काम नहीं कर रहा था।







application-state