จะบันทึกสถานะกิจกรรมโดยใช้บันทึกสถานะอินสแตนซ์ได้อย่างไร


2620

ฉันกำลังทำงานบนแพลตฟอร์ม Android SDK และยังไม่มีความชัดเจนในการบันทึกสถานะของแอปพลิเคชัน เพื่อให้ได้รับเครื่องมือใหม่เล็ก ๆ น้อย ๆ ของตัวอย่าง 'Hello, Android':

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

ฉันคิดว่ามันจะเพียงพอสำหรับกรณีที่ง่ายที่สุด แต่มันจะตอบกลับด้วยข้อความแรกเสมอไม่ว่าฉันจะออกจากแอพ

ฉันแน่ใจว่าวิธีแก้ปัญหานั้นง่ายพอ ๆ กับการเอาชนะonPauseหรืออะไรแบบนั้น แต่ฉันก็แอบดูเอกสารเป็นเวลา 30 นาทีและไม่พบอะไรที่ชัดเจน


9
เมื่อใดที่ saveInstanceState == null และเมื่อใดที่ไม่เป็นโมฆะ?
Trojan.ZBOT

90
คุณกำลังทำลายกิจกรรมของคุณโดยชัดเจน - ตามที่คุณพูดนำทางจากกิจกรรมเช่นโดยกดปุ่มย้อนกลับ ในความเป็นจริงสถานการณ์ที่ใช้ 'hiddenInstanceState' นี้คือเมื่อ Android ทำลายกิจกรรมของคุณเพื่อการพักผ่อนหย่อนใจ เพื่อจุดประสงค์: หากคุณเปลี่ยนภาษาของโทรศัพท์ในขณะที่กิจกรรมกำลังทำงาน (และจำเป็นต้องโหลดทรัพยากรที่แตกต่างจากโครงการของคุณ) อีกสถานการณ์ที่พบบ่อยมากคือเมื่อคุณหมุนโทรศัพท์ของคุณไปด้านข้างเพื่อให้กิจกรรมถูกสร้างขึ้นใหม่และแสดงในแนวนอน
villoren

16
หากต้องการรับข้อความที่สองให้เปิดใช้งาน "ไม่เก็บกิจกรรม" ในตัวเลือก dev กดปุ่มโฮมและกลับจากการอ่านล่าสุด
Yaroslav Mytkalyk


6
คุณสามารถทำได้ด้วย: onSaveInstanceState (บันเดิลที่บันทึกไว้InstanceState)
VahidHoseini

คำตอบ:


2568

คุณต้องแทนที่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.
}

Bundle นั้นเป็นวิธีการเก็บแผนที่ NVP ("ชื่อคู่ค่า") และมันจะถูกส่งผ่านไปonCreate()ยังและ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");
}

หรือจากส่วนที่

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

คุณมักจะใช้เทคนิคนี้เพื่อเก็บค่าอินสแตนซ์สำหรับแอปพลิเคชันของคุณ (การเลือกข้อความที่ไม่ได้บันทึก ฯลฯ )


24
โอกาสใดก็ได้ที่ใช้งานโทรศัพท์ แต่ไม่ได้อยู่ในโปรแกรมจำลอง ฉันไม่สามารถรับข้อมูลที่ไม่ใช่ null ได้
Adam Jack เมื่อ

491
ข้อควรระวัง: คุณต้องโทรหา super.onSaveInstanceState (บันทึกไว้ IncidentState) ก่อนที่จะเพิ่มค่าของคุณไปยัง Bundle มิฉะนั้นพวกเขาจะถูกลบออกจากการโทรนั้น (Droid X Android 2.2)
jkschneider

121
ข้อควรระวัง: เอกสารอย่างเป็นทางการระบุว่าคุณควรบันทึกข้อมูลสำคัญไว้ใน onPause-Method เนื่องจากวิธี onsaveinstance- ไม่ใช่ส่วนหนึ่งของวงจรชีวิตของ android developer.android.com/reference/android/app/Activity.html
schlingel

32
ความจริงนั้นมีonSaveInstanceStateประโยชน์เกือบไร้ประโยชน์ยกเว้นกรณีที่มีการเปลี่ยนแปลงการวางแนวหน้าจอ ในเกือบทุกกรณีคุณไม่สามารถเชื่อถือได้และจะต้องบันทึกสถานะ UI ของคุณด้วยตนเองที่อื่น หรือป้องกันไม่ให้แอปของคุณถูกฆ่าโดยการเอาชนะพฤติกรรมของปุ่ม BACK ฉันไม่เข้าใจว่าทำไมพวกเขาถึงใช้งานมันตั้งแต่แรก ใช้งานง่ายโดยสิ้นเชิง และคุณไม่มี Bundle ที่ระบบจะให้คุณบันทึกสิ่งต่าง ๆ ยกเว้นในวิธีการเฉพาะนี้
chakrit

12
โปรดทราบว่าการบันทึก / เรียกคืนสถานะ UI ไปยัง / จาก Bundle จะได้รับการดูแลโดยอัตโนมัติสำหรับViews ที่ได้รับรหัสประจำตัว จากonSaveInstanceStateเอกสาร: "การใช้งานเริ่มต้นจะดูแลสถานะ UI ต่ออินสแตนซ์ส่วนใหญ่ให้คุณโดยการเรียกonSaveInstanceState()แต่ละมุมมองในลำดับชั้นที่มี ID และโดยการบันทึก id ของมุมมองที่เน้นอยู่ในปัจจุบัน (ซึ่งทั้งหมดจะถูกกู้คืน โดยการใช้งานเริ่มต้นของonRestoreInstanceState(Bundle)) "
Vicky Chijwani

433

savedInstanceStateเป็นเพียงสำหรับการบันทึกของรัฐที่เกี่ยวข้องกับอินสแตนซ์ปัจจุบันของกิจกรรมเช่นนำทางปัจจุบันหรือข้อมูลการเลือกเพื่อที่ว่าถ้าทำลาย Android และสร้างกิจกรรมก็สามารถกลับมาเป็นเหมือนเดิมก่อน ดูเอกสารประกอบสำหรับonCreateและonSaveInstanceState

สำหรับสถานะที่มีอายุการใช้งานนานขึ้นให้พิจารณาใช้ฐานข้อมูล SQLite ไฟล์หรือการกำหนดค่าตามความชอบ ดูประหยัดรัฐต่อเนื่อง


3
เมื่อใดที่ saveInstanceState == null และเมื่อใดที่ไม่เป็นโมฆะ?
Trojan.ZBOT

6
saveInstanceState เป็นโมฆะเมื่อระบบกำลังสร้างอินสแตนซ์ใหม่ของกิจกรรมของคุณและไม่เป็นโมฆะเมื่อทำการกู้คืน
Gabriel Câmara

7
... ซึ่งทำให้เกิดคำถามของเมื่อไม่จำเป็นต้องมีระบบการสร้างตัวอย่างใหม่ของกิจกรรม วิธีการออกจากแอปบางอย่างไม่ได้สร้างมัดดังนั้นจึงต้องสร้างอินสแตนซ์ใหม่ นี่คือปัญหาพื้นฐาน มันหมายความว่าไม่มีใครสามารถพึ่งพาการมีอยู่ของมัดและต้องทำวิธีการทางเลือกบางอย่างของการจัดเก็บถาวร ประโยชน์ของ onSave / onRestoreInstanceState คือมันเป็นกลไกที่ระบบสามารถทำได้อย่างฉับพลันโดยไม่ต้องใช้ทรัพยากรระบบมากนัก ดังนั้นจึงเป็นการดีที่จะสนับสนุนและมีพื้นที่เก็บข้อมูลถาวรสำหรับการออกจากแอพที่สง่างามมากขึ้น
ToolmakerSteve

415

หมายเหตุว่ามันไม่ปลอดภัยในการใช้งานonSaveInstanceStateและonRestoreInstanceState ข้อมูลถาวรตามเอกสารเกี่ยวกับรัฐกิจกรรมในhttp://developer.android.com/reference/android/app/Activity.html

สถานะเอกสาร (ในส่วน 'กิจกรรมวงจรชีวิต'):

โปรดทราบว่ามันเป็นสิ่งสำคัญที่จะบันทึกข้อมูลถาวรonPause()แทนonSaveInstanceState(Bundle) เพราะภายหลังไม่ได้เป็นส่วนหนึ่งของวงจรการเรียกกลับดังนั้นจะไม่ถูกเรียกในทุกสถานการณ์ตามที่อธิบายไว้ในเอกสารประกอบ

กล่าวอีกนัยหนึ่งให้ใส่รหัสบันทึก / กู้คืนสำหรับข้อมูลถาวรในonPause()และonResume()!

แก้ไข : เพื่อชี้แจงเพิ่มเติมนี่คือonSaveInstanceState()เอกสาร:

วิธีการนี้เรียกว่าก่อนที่กิจกรรมอาจถูกฆ่าเพื่อที่ว่าเมื่อมันกลับมาอีกครั้งในอนาคตจะสามารถเรียกคืนสถานะของมันได้ ตัวอย่างเช่นหากกิจกรรม B เปิดตัวต่อหน้ากิจกรรม A และในบางกิจกรรมที่ A ถูกฆ่าเพื่อเรียกคืนทรัพยากรกิจกรรม A จะมีโอกาสบันทึกสถานะปัจจุบันของส่วนต่อประสานผู้ใช้ผ่านวิธีนี้เพื่อให้เมื่อผู้ใช้ส่งคืน กิจกรรม A, รัฐของอินเตอร์เฟซผู้ใช้สามารถเรียกคืนผ่านหรือonCreate(Bundle) onRestoreInstanceState(Bundle)


55
เพียงเพื่อ nitpick: มันไม่ปลอดภัยเช่นกัน สิ่งนี้ขึ้นอยู่กับสิ่งที่คุณต้องการรักษาและนานเท่าไหร่ @Bernard ที่ไม่ชัดเจนในคำถามดั้งเดิมของเขา InstanceState เหมาะสำหรับการรักษาสถานะ UI ปัจจุบัน (ข้อมูลที่ป้อนในการควบคุมตำแหน่งปัจจุบันในรายการและอื่น ๆ ) ในขณะที่ Pause / Resume เป็นไปได้เพียงอย่างเดียวสำหรับการจัดเก็บข้อมูลถาวรในระยะยาว
Pontus Gagge

30
ควร downvote นี้ ไม่ปลอดภัยที่จะใช้ใน (บันทึก | กู้คืน) InstanceState เช่นวิธีวงจรชีวิต (เช่นทำสิ่งอื่นในพวกเขามากกว่าบันทึก / กู้คืนสถานะ) เหมาะอย่างยิ่งสำหรับการบันทึก / เรียกคืนสถานะ นอกจากนี้คุณต้องการบันทึก / เรียกคืนสถานะใน onPause และ onResume อย่างไร คุณไม่ได้รับการรวมกลุ่มในวิธีการที่คุณสามารถใช้ดังนั้นคุณต้องใช้การประหยัดสถานะอื่น ๆ ในฐานข้อมูลไฟล์และอื่น ๆ ที่โง่
เฟลิกซ์

141
เราไม่ควรลงคะแนนบุคคลนี้อย่างน้อยเขาก็พยายามอ่านเอกสารและฉันคิดว่าเราทุกคนอยู่ที่นี่เพื่อสร้างชุมชนที่มีความรู้และช่วยเหลือซึ่งกันและกันไม่ให้ลงคะแนน ดังนั้น 1 โหวตสำหรับความพยายามและฉันจะขอให้คุณคนที่จะไม่ลงคะแนนค่อนข้างโหวตขึ้นหรือไม่ลงคะแนน .... คนนี้ล้างความสับสนที่เราอยากได้เมื่อผ่านเอกสาร 1 โหวตขึ้น :)
AZ_

21
ฉันไม่คิดว่าคำตอบนี้สมควรได้รับ downvote อย่างน้อยเขาก็พยายามที่จะตอบและได้เสนอหัวข้อจาก doco
GSree

34
คำตอบนี้ถูกต้องอย่างแน่นอนและสมควรได้รับการโหวตสูงขึ้นไม่ลง! ให้ฉันอธิบายความแตกต่างระหว่างรัฐสำหรับพวกที่ไม่เห็นมัน สถานะ GUI เช่นปุ่มตัวเลือกและข้อความบางอย่างในฟิลด์อินพุตมีความสำคัญน้อยกว่าสถานะข้อมูลเช่นบันทึกที่เพิ่มลงในรายการที่แสดงใน ListView หลังต้องถูกจัดเก็บไปยังฐานข้อมูลใน onPause เนื่องจากเป็นการโทรที่รับประกันเท่านั้น หากคุณใส่ไว้ใน OnSaveInstanceState แทนคุณจะเสี่ยงต่อการสูญเสียข้อมูลหากไม่ได้ถูกเรียก แต่หากการเลือกปุ่มตัวเลือกไม่ได้รับการบันทึกด้วยเหตุผลเดียวกัน - ไม่ใช่เรื่องใหญ่
JBM

206

เพื่อนร่วมงานของฉันเขียนบทความอธิบายรัฐแอพลิเคชันบนอุปกรณ์ Android รวมทั้งคำอธิบายเกี่ยวกับวงจรชีวิตของข้อมูลกิจกรรมและรัฐวิธีการจัดเก็บข้อมูลของรัฐและประหยัดให้กับรัฐBundleและSharedPreferencesและดูที่นี่

บทความครอบคลุมสามแนวทาง:

เก็บข้อมูลการควบคุมตัวแปรภายใน / UI สำหรับอายุการใช้งานของแอปพลิเคชั่น (เช่นชั่วคราว) โดยใช้ชุดสถานะอินสแตนซ์

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

จัดเก็บข้อมูลตัวแปรโลคัล / UI ควบคุมระหว่างอินสแตนซ์ของแอปพลิเคชัน (เช่นถาวร) โดยใช้การกำหนดลักษณะที่แบ่งปัน

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

3
@ MartinBelcher-Eigo Article พูดถึงข้อมูลใน SharedPreferences ว่า "ข้อมูลนี้ถูกเขียนไปยังฐานข้อมูลบนอุปกรณ์ .. " ฉันเชื่อว่าข้อมูลนั้นถูกเก็บไว้ในไฟล์ในไดเรกทอรีของแอปในระบบไฟล์
ทอม

2
ข้อมูล @Tom SharefPrefs ถูกเขียนไปยังไฟล์ xml xml เป็นฐานข้อมูลหรือไม่ ฉันจะบอกว่ามันเป็น;)
MaciejGórski

148

นี่คือ 'gotcha' แบบดั้งเดิมของการพัฒนา Android มีสองประเด็นที่นี่:

  • มีข้อบกพร่องเล็ก ๆ น้อย ๆ ของ Android Framework ซึ่งทำให้การจัดการสแต็กแอปพลิเคชั่นซับซ้อนขึ้นอย่างมากระหว่างการพัฒนาอย่างน้อยในรุ่นเก่า (ไม่แน่ใจว่าเมื่อใด / เมื่อใด / มันถูกแก้ไขอย่างไร) ฉันจะหารือข้อผิดพลาดนี้ด้านล่าง
  • วิธี 'ปกติ' หรือที่ตั้งใจจะจัดการกับปัญหานี้คือตัวเองค่อนข้างซับซ้อนกับคู่ของ onPause / onResume และ onSaveInstanceState / onRestoreInstanceState

เมื่อดูหัวข้อเหล่านี้ทั้งหมดฉันสงสัยว่าหลายครั้งที่นักพัฒนากำลังพูดถึงปัญหาที่แตกต่างกันสองอย่างพร้อมกัน ... ดังนั้นความสับสนและรายงานของ "สิ่งนี้ไม่ได้ผลสำหรับฉัน"

ก่อนอื่นให้ชี้แจงพฤติกรรมที่ 'ตั้งใจ': onSaveInstance และ onRestoreInstance นั้นบอบบางและใช้สำหรับสถานะชั่วคราวเท่านั้น การใช้งานตามวัตถุประสงค์ (afaict) คือการจัดการกิจกรรมสันทนาการเมื่อโทรศัพท์หมุน (เปลี่ยนทิศทาง) กล่าวอีกนัยหนึ่งการใช้งานที่ตั้งใจคือเมื่อกิจกรรมของคุณยังคงเป็น 'อยู่ด้านบน' อย่างมีเหตุผล แต่ยังคงต้องมีการคืนสถานะโดยระบบ บันเดิลที่บันทึกไว้จะไม่ถูกเก็บไว้ภายนอกกระบวนการ / หน่วยความจำ / gc ดังนั้นคุณจึงไม่สามารถไว้วางใจได้หากกิจกรรมของคุณไปที่พื้นหลัง ใช่บางทีหน่วยความจำของกิจกรรมของคุณจะรอดชีวิตจากการเดินทางไปที่พื้นหลังและหลบหนี GC แต่สิ่งนี้ไม่น่าเชื่อถือ (และไม่สามารถคาดเดาได้)

ดังนั้นหากคุณมีสถานการณ์ที่มี 'ความคืบหน้าของผู้ใช้' หรือรัฐที่มีความหมายซึ่งควรจะคงอยู่ระหว่าง 'การเปิดตัว' ของแอปพลิเคชันของคุณคำแนะนำคือการใช้ onPause และ onResume คุณต้องเลือกและเตรียมร้านค้าถาวรด้วยตนเอง

แต่มีข้อผิดพลาดที่สับสนมากซึ่งทำให้สิ่งเหล่านี้ซับซ้อน รายละเอียดอยู่ที่นี่:

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

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

โดยทั่วไปหากแอปพลิเคชันของคุณเปิดใช้งานด้วยการตั้งค่าสถานะ SingleTask จากนั้นคุณจะเปิดใช้งานได้จากหน้าจอหลักหรือเมนูตัวเรียกใช้การเรียกใช้ครั้งต่อไปนั้นจะเป็นการสร้างงานใหม่ ... คุณจะมีสองอินสแตนซ์ที่แตกต่างกัน พำนักอยู่ในกองเดียวกัน ... ซึ่งแปลกอย่างรวดเร็วมาก สิ่งนี้ดูเหมือนจะเกิดขึ้นเมื่อคุณเปิดแอประหว่างการพัฒนา (เช่นจาก Eclipse หรือ Intellij) ดังนั้นนักพัฒนาจึงพบเจอสิ่งนี้มากมาย แต่ยังผ่านกลไกการอัปเดตบางตัวของ app store (ซึ่งจะส่งผลกระทบต่อผู้ใช้ของคุณเช่นกัน)

ฉันต่อสู้ผ่านเธรดเหล่านี้หลายชั่วโมงก่อนที่ฉันจะรู้ว่าปัญหาหลักของฉันคือข้อผิดพลาดนี้ไม่ใช่พฤติกรรมของเฟรมเวิร์ก เขียนที่ดีและวิธีแก้ปัญหา (ปรับปรุง: ดูด้านล่าง) น่าจะมาจากผู้ใช้ @kaciula ในคำตอบนี้:

พฤติกรรมการกดปุ่มโฮม

อัปเดตมิถุนายน 2556 : หลายเดือนต่อมาในที่สุดฉันก็พบโซลูชัน 'ถูกต้อง' คุณไม่จำเป็นต้องจัดการการตั้งค่าสถานะแอปเริ่มต้นใด ๆ ด้วยตัวคุณเองคุณสามารถตรวจจับสิ่งนี้จากกรอบงานและประกันตัวได้อย่างเหมาะสม ฉันใช้สิ่งนี้ใกล้กับจุดเริ่มต้นของ 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;
    }
}

87

onSaveInstanceStateถูกเรียกเมื่อระบบต้องการหน่วยความจำและฆ่าแอปพลิเคชัน มันไม่ได้ถูกเรียกเมื่อผู้ใช้เพิ่งปิดแอปพลิเคชัน ดังนั้นฉันคิดว่าสถานะแอปพลิเคชันควรได้รับการบันทึกไว้ในonPauseควรจะบันทึกลงในที่เก็บข้อมูลถาวรบางอย่างเช่นPreferencesหรือSqlite


36
ขออภัยนั่นไม่ถูกต้องนัก onSaveInstanceState ได้รับการเรียกก่อนที่กิจกรรมจะต้องทำใหม่ คือทุกครั้งที่ผู้ใช้หมุนอุปกรณ์ มันมีไว้สำหรับการจัดเก็บสถานะมุมมองชั่วคราว เมื่อ android บังคับให้ปิดแอปพลิเคชัน onSaveInstanceState จะไม่ถูกเรียกใช้ (ซึ่งเป็นสาเหตุที่ทำให้ไม่ปลอดภัยสำหรับการจัดเก็บข้อมูลแอปพลิเคชันที่สำคัญ) onPause อย่างไรก็ตามมีการรับประกันว่าจะถูกเรียกก่อนที่กิจกรรมจะถูกทำลายดังนั้นจึงควรใช้ในการจัดเก็บข้อมูลถาวรในการตั้งค่าหรือ Squlite คำตอบที่ถูกต้องด้วยเหตุผลที่ผิด
moveaway00

74

ทั้งสองวิธีมีประโยชน์และถูกต้องและเหมาะสมที่สุดสำหรับสถานการณ์ต่าง ๆ :

  1. ผู้ใช้ยกเลิกแอปพลิเคชันและเปิดใหม่อีกครั้งในภายหลัง แต่แอปพลิเคชันจำเป็นต้องโหลดข้อมูลจากเซสชันล่าสุด - ต้องใช้วิธีการจัดเก็บข้อมูลแบบถาวรเช่นการใช้ SQLite
  2. ผู้ใช้สลับแอปพลิเคชันจากนั้นกลับมาที่ต้นฉบับและต้องการรับที่ที่ค้างไว้ - บันทึกและกู้คืนข้อมูลชุดรวม (เช่นข้อมูลสถานะแอปพลิเคชัน) ในonSaveInstanceState()และonRestoreInstanceState()มักจะเพียงพอ

หากคุณบันทึกข้อมูลสถานะในลักษณะถาวรข้อมูลนั้นสามารถโหลดใหม่ในonResume()หรือonCreate()(หรือจริง ๆ ในการเรียกใช้รอบอายุใด ๆ ) นี่อาจเป็นพฤติกรรมที่ต้องการหรือไม่ก็ได้ หากคุณเก็บไว้ในกลุ่มในInstanceStateก็จะชั่วคราวและเหมาะสำหรับการจัดเก็บข้อมูลเพื่อใช้ใน 'เซสชัน' ผู้ใช้คนเดียวกัน (ฉันใช้คำว่าเซสชันอย่างหลวม ๆ ) แต่ไม่ใช่ระหว่าง 'เซสชัน'

ไม่ใช่ว่าวิธีการหนึ่งจะดีกว่าวิธีอื่นเช่นเดียวกับทุกอย่างมันเป็นสิ่งสำคัญที่จะต้องเข้าใจพฤติกรรมที่คุณต้องการและเลือกวิธีการที่เหมาะสมที่สุด


70

สถานะการออมเป็นสิ่งที่ดีที่สุดเท่าที่ฉันกังวล หากคุณต้องการบันทึกข้อมูลถาวรเพียงใช้ฐานข้อมูลSQLite Android ทำให้การใช้งานSOOOง่ายขึ้น

บางสิ่งเช่นนี้

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;

9
เนื่องจากใช้เวลานานเกินไปในการโหลดฐานข้อมูล SQLite การพิจารณาว่านี่เป็นเส้นทางสำคัญในการแสดง UI ของแอปให้กับผู้ใช้ ฉันไม่ได้ตั้งเวลาจริง ๆ ดังนั้นฉันยินดีที่จะแก้ไข แต่แน่นอนว่าการโหลดและเปิดไฟล์ฐานข้อมูลจะไม่เร็ว
ทอม

5
ขอบคุณมากสำหรับการมอบโซลูชั่นที่มือใหม่สามารถตัดและวางลงในแอพและใช้งานได้ทันที! @ Tom เท่าที่ความเร็วใช้เวลาประมาณเจ็ดวินาทีในการเก็บ 1,000 คู่ แต่คุณสามารถทำได้ใน AsyncTask อย่างไรก็ตามคุณต้องเพิ่ม {cursor.close ()} ในที่สุดหรืออาจเกิดข้อผิดพลาดจากหน่วยความจำรั่วขณะทำสิ่งนี้
Noumenon

3
ฉันเจอสิ่งนี้และในขณะที่มันดูเรียบร้อยฉันลังเลที่จะลองใช้สิ่งนี้กับ Google Glass ซึ่งเป็นอุปกรณ์ที่ฉันใช้งาน / เมื่อเร็ว ๆ นี้
Stephen Tetreault

61

ฉันคิดว่าฉันพบคำตอบ ให้ฉันบอกสิ่งที่ฉันทำในคำง่าย ๆ :

สมมติว่าฉันมีสองกิจกรรม, กิจกรรม 1 และกิจกรรม 2 และฉันกำลังนำทางจากกิจกรรม 1 ถึงกิจกรรม 2 (ฉันทำงานบางอย่างในกิจกรรม 2) แล้วกลับไปทำกิจกรรมที่ 1 อีกครั้งโดยคลิกที่ปุ่มในกิจกรรม 1 ในขั้นตอนนี้ฉันต้องการกลับไปที่กิจกรรม 2 และฉันต้องการดูกิจกรรมของฉัน 2 ในสภาพเดียวกันเมื่อฉันออกจากกิจกรรมล่าสุด 2

สำหรับสถานการณ์ข้างต้นสิ่งที่ฉันได้ทำคือในรายการฉันได้ทำการเปลี่ยนแปลงบางอย่างเช่นนี้:

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

และในกิจกรรม 1 ของเหตุการณ์การคลิกปุ่มฉันได้ทำสิ่งนี้:

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

และในกิจกรรม click2 ที่ปุ่มฉันได้ทำสิ่งนี้:

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

ตอนนี้สิ่งที่จะเกิดขึ้นคือสิ่งที่การเปลี่ยนแปลงที่เราทำในกิจกรรม 2 จะไม่สูญหายไปและเราสามารถดูกิจกรรม 2 ในสถานะเดียวกับที่เราออกไปก่อนหน้านี้

ฉันเชื่อว่านี่คือคำตอบและมันก็ใช้ได้ดีสำหรับฉัน ถูกต้องฉันถ้าฉันผิด


2
@bagusflyer สนใจเฉพาะเจาะจงมากขึ้น ??? ความคิดเห็นของคุณไม่มีประโยชน์และไม่มีใครสามารถช่วยคุณได้
Stephen Tetreault

2
นี่คือคำตอบของสถานการณ์ที่แตกต่าง: สองกิจกรรมภายในแอพเดียวกัน OP เกี่ยวกับการออกจากแอพ (เช่นปุ่มโฮมหรือวิธีการอื่นเพื่อสลับไปยังแอพอื่น)
ToolmakerSteve

44

onSaveInstanceState()สำหรับข้อมูลชั่วคราว (กู้คืนในonCreate()/ onRestoreInstanceState()), onPause()สำหรับข้อมูลถาวร (กู้คืนในonResume()) จากแหล่งข้อมูลทางเทคนิคของ Android:

onSaveInstanceState ()ถูกเรียกโดย Android หากกิจกรรมหยุดทำงานและอาจถูกฆ่าก่อนที่จะกลับมาทำงานต่อ! ซึ่งหมายความว่าควรเก็บสถานะใด ๆ ที่จำเป็นเพื่อเริ่มต้นใหม่ให้อยู่ในสภาพเดียวกันเมื่อเริ่มกิจกรรมใหม่ เป็นคู่กับวิธี onCreate () และอันที่จริงบันเดิลที่บันทึกไว้จะถูกส่งผ่านไปยัง onCreate () เป็น Bundle เดียวกันกับที่คุณสร้างเป็น outState ในเมธอด onSaveInstanceState ()

onPause ()และonResume ()เป็นวิธีฟรี onPause () จะถูกเรียกเสมอเมื่อกิจกรรมสิ้นสุดลงแม้ว่าเราจะให้ความสำคัญกับการโทร (ด้วยการสิ้นสุด ()) เราจะใช้สิ่งนี้เพื่อบันทึกโน้ตปัจจุบันกลับไปที่ฐานข้อมูล แนวปฏิบัติที่ดีคือการปล่อยทรัพยากรใด ๆ ที่สามารถปล่อยออกมาในระหว่าง onPause () ได้เช่นกันเพื่อใช้ทรัพยากรน้อยลงเมื่ออยู่ในสถานะแฝง


40

onSaveInstanceState()ถูกเรียกจริงๆเมื่อกิจกรรมเข้าสู่พื้นหลัง

อ้างจากเอกสาร: "วิธีการนี้เรียกว่าก่อนที่กิจกรรมอาจถูกฆ่าเพื่อที่ว่าเมื่อมันกลับมาบางครั้งในอนาคตมันสามารถเรียกคืนสถานะของมัน" แหล่ง


37

เพื่อช่วยลดสำเร็จรูปสำเร็จรูปฉันใช้ต่อไปนี้interfaceและclassเพื่ออ่าน / เขียนไปยัง a 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ซึ่งได้รับใบอนุญาตภายใต้ใบอนุญาตเอ็มไอที


34

ในขณะเดียวกันฉันทำโดยทั่วไปไม่มีประโยชน์

Bundle savedInstanceState & Co

วงจรชีวิตสำหรับกิจกรรมส่วนใหญ่ซับซ้อนเกินไปและไม่จำเป็น

และ Google ระบุตัวเองมันไม่น่าเชื่อถือ

วิธีของฉันคือบันทึกการเปลี่ยนแปลงใด ๆ ทันทีในการตั้งค่า:

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

SharedPreferences นั้นคล้ายกับการรวมกลุ่ม และโดยธรรมชาติและในตอนแรกค่าดังกล่าวจะต้องอ่านจากการตั้งค่า

ในกรณีของข้อมูลที่ซับซ้อนคุณสามารถใช้ SQLite แทนการใช้การตั้งค่า

เมื่อใช้แนวคิดนี้กิจกรรมจะยังคงใช้สถานะที่บันทึกไว้ล่าสุดไม่ว่าจะเป็นการเปิดครั้งแรกโดยมีการรีบูตในระหว่างหรือเปิดอีกครั้งเนื่องจากสแตกด้านหลัง


31

เพื่อตอบคำถามเดิมโดยตรง saveInstancestate เป็นโมฆะเพราะกิจกรรมของคุณจะไม่ถูกสร้างขึ้นใหม่

กิจกรรมของคุณจะถูกสร้างขึ้นใหม่ด้วยกลุ่มสถานะเมื่อ:

  • การเปลี่ยนแปลงการกำหนดค่าเช่นการเปลี่ยนการวางแนวหรือภาษาของโทรศัพท์ซึ่งอาจต้องมีการสร้างอินสแตนซ์กิจกรรมใหม่
  • คุณกลับสู่แอพจากพื้นหลังหลังจากที่ระบบปฏิบัติการได้ทำลายกิจกรรม

Android จะทำลายกิจกรรมพื้นหลังเมื่ออยู่ภายใต้แรงกดดันหน่วยความจำหรือหลังจากที่พวกเขาอยู่ในพื้นหลังเป็นระยะเวลานาน

เมื่อทดสอบตัวอย่างสวัสดีชาวโลกของคุณมีวิธีไม่กี่วิธีที่จะออกไปและกลับไปที่กิจกรรม

  • เมื่อคุณกดปุ่มย้อนกลับกิจกรรมจะเสร็จสิ้น การเรียกใช้แอปซ้ำอีกครั้งเป็นอินสแตนซ์ใหม่ล่าสุด คุณไม่ได้ดำเนินการต่อจากพื้นหลังเลย
  • เมื่อคุณกดปุ่มโฮมหรือใช้ตัวสลับงานกิจกรรมจะเข้าสู่พื้นหลัง เมื่อนำทางกลับไปที่แอปพลิเคชั่น onCreate จะถูกเรียกเฉพาะเมื่อกิจกรรมต้องถูกทำลาย

ในกรณีส่วนใหญ่หากคุณเพิ่งกดปุ่มโฮมแล้วเปิดแอพอีกครั้งกิจกรรมไม่จำเป็นต้องถูกสร้างขึ้นใหม่ มีอยู่ในหน่วยความจำอยู่แล้วดังนั้น onCreate () จะไม่ถูกเรียก

มีตัวเลือกภายใต้การตั้งค่า -> ตัวเลือกสำหรับนักพัฒนาที่เรียกว่า "อย่าเก็บกิจกรรม" เมื่อเปิดใช้งาน Android จะทำลายกิจกรรมและสร้างมันใหม่เมื่อมีพื้นหลัง นี่เป็นตัวเลือกที่ยอดเยี่ยมในการเปิดใช้งานเมื่อมีการพัฒนาเพราะมันจำลองสถานการณ์กรณีที่เลวร้ายที่สุด (อุปกรณ์หน่วยความจำเหลือน้อยรีไซเคิลกิจกรรมของคุณตลอดเวลา)

คำตอบอื่น ๆ มีค่าเมื่อพวกเขาสอนวิธีที่ถูกต้องในการจัดเก็บสถานะ แต่ฉันไม่รู้สึกว่าพวกเขาตอบคำถามจริงๆเพราะรหัสของคุณไม่ทำงานในแบบที่คุณคาดหวัง


28

onSaveInstanceState(bundle)และonRestoreInstanceState(bundle)วิธีการจะมีประโยชน์สำหรับการติดตาข้อมูลเพียงในขณะที่หมุนหน้าจอ (เปลี่ยนแปลงการวางแนว)
พวกเขาจะไม่ได้ดีในขณะที่สลับระหว่างแอปพลิเคชัน (เนื่องจากonSaveInstanceState()วิธีการที่เรียกว่า แต่onCreate(bundle)และonRestoreInstanceState(bundle)จะไม่ถูกเรียกอีกครั้ง
สำหรับความคงทนมากขึ้นใช้การตั้งค่าที่ใช้ร่วมกันอ่านบทความนี้


2
ในกรณีของคุณonCreateและonRestoreInstanceStateไม่ได้ถูกเรียกเนื่องจากActivityไม่ถูกทำลายเมื่อคุณสลับแอปดังนั้นไม่จำเป็นต้องกู้คืนอะไร Android เรียกonSaveInstanceStateในกรณีที่กิจกรรมถูกทำลายในภายหลัง (ซึ่งเกิดขึ้นกับ 100% แน่นอนเมื่อหมุนหน้าจอเนื่องจากการกำหนดค่าอุปกรณ์ทั้งหมดมีการเปลี่ยนแปลงและกิจกรรมจะต้องถูกสร้างขึ้นใหม่ตั้งแต่ต้น)
Vicky Chijwani

20

ปัญหาของฉันคือฉันต้องการความคงอยู่เฉพาะในช่วงอายุการใช้งานของแอปพลิเคชัน (เช่นการดำเนินการครั้งเดียวรวมถึงการเริ่มกิจกรรมย่อยอื่น ๆ ภายในแอพเดียวกันและหมุนอุปกรณ์เป็นต้น) ฉันลองชุดคำตอบข้างต้นหลายแบบ แต่ไม่ได้สิ่งที่ฉันต้องการในทุกสถานการณ์ ในท้ายที่สุดสิ่งที่ใช้ได้ผลสำหรับฉันคือการได้รับการอ้างอิงไปยังที่บันทึกไว้InstanceStateในระหว่างการสร้าง:

mySavedInstanceState=savedInstanceState;

และใช้สิ่งนั้นเพื่อรับเนื้อหาของตัวแปรของฉันเมื่อฉันต้องการมันตามแนวของ:

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

ฉันใช้onSaveInstanceStateและonRestoreInstanceStateตามที่แนะนำข้างต้น แต่ฉันเดาว่าฉันสามารถใช้วิธีของฉันหรืออีกวิธีหนึ่งในการบันทึกตัวแปรเมื่อมีการเปลี่ยนแปลง (เช่นใช้putBoolean)


19

ถึงแม้ว่าคำตอบที่ได้รับการยอมรับเป็นที่ถูกต้องมีความเป็นไปได้เร็วขึ้นและวิธีการที่ง่ายต่อการบันทึกกิจกรรมรัฐบน Android การใช้ห้องสมุดที่เรียกว่าIcepick Icepick เป็นโปรเซสเซอร์คำอธิบายประกอบที่ดูแลรหัสสำเร็จรูปทั้งหมดที่ใช้ในการบันทึกและกู้คืนสถานะให้กับคุณ

ทำอะไรแบบนี้กับ 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);
  }
}

เหมือนกับการทำสิ่งนี้:

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 จะทำงานร่วมกับวัตถุใด ๆ Bundleที่จะช่วยประหยัดรัฐด้วย


16

เมื่อกิจกรรมถูกสร้างขึ้นจะมีการเรียกใช้เมธอด onCreate ()

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

saveInstanceState เป็นวัตถุของคลาส Bundle ซึ่งเป็นโมฆะในครั้งแรก แต่มีค่าเมื่อสร้างขึ้นใหม่ ในการบันทึกสถานะของกิจกรรมคุณต้องแทนที่ onSaveInstanceState ()

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

ใส่ค่าของคุณในวัตถุชุด "outState" เช่น outState.putString ("คีย์", "ยินดีต้อนรับกลับ") และบันทึกโดยเรียก super เมื่อกิจกรรมจะถูกทำลายสถานะของมันจะถูกบันทึกไว้ในวัตถุ Bundle และสามารถกู้คืนได้หลังจากการพักผ่อนใน onCreate () หรือ onRestoreInstanceState () ได้รับ Bundle ใน onCreate () และ onRestoreInstanceState () เหมือนกัน

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

15

โดยทั่วไปมีสองวิธีในการใช้การเปลี่ยนแปลงนี้

  1. ใช้และonSaveInstanceState()onRestoreInstanceState()
  2. android:configChanges="orientation|screenSize"ในที่ประจักษ์

ฉันไม่แนะนำให้ใช้วิธีที่สอง เนื่องจากหนึ่งในประสบการณ์ของฉันมันทำให้ครึ่งหนึ่งของหน้าจออุปกรณ์ดำในขณะที่หมุนจากแนวตั้งเป็นแนวนอนและในทางกลับกัน

ใช้วิธีแรกที่กล่าวถึงข้างต้นเราสามารถเก็บข้อมูลไว้ได้เมื่อมีการเปลี่ยนทิศทางหรือเปลี่ยนการกำหนดค่าใด ๆ ฉันรู้วิธีที่คุณสามารถเก็บข้อมูลประเภทใด ๆ ไว้ในวัตถุสถานะที่บันทึกไว้ของวัตถุ

ตัวอย่าง: พิจารณากรณีและปัญหาหากคุณต้องการคงวัตถุ Json ไว้ สร้างคลาสโมเดลด้วย getters และ setters

class MyModel extends Serializable{
JSONObject obj;

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

JSONObject getJsonObject()
return this.obj;
} 
}

ตอนนี้ในกิจกรรมของคุณในวิธี onCreate และ onSaveInstanceState ให้ทำดังต่อไปนี้ มันจะมีลักษณะดังนี้:

@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

นี่คือความคิดเห็นจากคำตอบของSteve Moseley (โดยToolmakerSteve ) ที่ทำให้สิ่งต่าง ๆ กลายเป็นมุมมอง (ในภาพรวมทั้งหมดเกี่ยวกับ SaveInstanceState vs onPause, ค่าใช้จ่ายทางทิศตะวันออกและค่าใช้จ่ายทางทิศตะวันตก)

@VVK - ฉันไม่เห็นด้วยบางส่วน บางวิธีในการออกจากแอพไม่เรียกใช้ OnSaveInstanceState (oSIS) สิ่งนี้ จำกัด ประโยชน์ของ oSIS การสนับสนุนที่คุ้มค่าสำหรับทรัพยากรระบบปฏิบัติการที่น้อยที่สุด แต่หากแอพต้องการคืนผู้ใช้กลับสู่สถานะที่พวกเขาอยู่ไม่ว่าจะออกจากแอปนั้นอย่างไรก็จำเป็นต้องใช้วิธีการจัดเก็บข้อมูลแบบถาวรแทน ฉันใช้ onCreate เพื่อตรวจสอบบันเดิลและถ้ามันหายไปให้ตรวจสอบที่ เก็บข้อมูลถาวร สิ่งนี้ทำให้การตัดสินใจเป็นศูนย์กลาง ฉันสามารถกู้คืนจากความผิดพลาดหรือออกจากปุ่มย้อนกลับหรือออกจากรายการเมนูที่กำหนดเองหรือกลับไปที่หน้าจอผู้ใช้ในหลายวันต่อมา - ToolmakerSteve 19 ก.ย. 2558 เวลา 10:38 น


10

รหัส Kotlin:

ประหยัด:

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

แล้วในonCreate()หรือonRestoreInstanceState()

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

เพิ่มค่าเริ่มต้นหากคุณไม่ต้องการมีตัวเลือก


9

ในการรับข้อมูลสถานะกิจกรรมที่เก็บไว้ในขั้นonCreate()แรกคุณต้องบันทึกข้อมูลใน hiddenInstanceState โดยSaveInstanceState(Bundle savedInstanceState)วิธีการแทนที่

เมื่อSaveInstanceState(Bundle savedInstanceState)วิธีการทำลายกิจกรรมได้รับการเรียกและคุณบันทึกข้อมูลที่คุณต้องการบันทึก และคุณจะได้รับเหมือนเดิมonCreate()เมื่อกิจกรรมเริ่มต้นใหม่ (saveInstanceState จะไม่เป็นโมฆะเนื่องจากคุณได้บันทึกข้อมูลไว้ก่อนที่กิจกรรมจะถูกทำลาย)


6

ง่าย ๆ ในการแก้ไขปัญหานี้คือการใช้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);
  }
}

มันใช้งานได้สำหรับกิจกรรมชิ้นส่วนหรือวัตถุใด ๆ ที่จำเป็นต้องทำให้เป็นอันดับรัฐใน Bundle (เช่น ViewPresenters ของครก)

Icepick ยังสามารถสร้างรหัสสถานะอินสแตนซ์สำหรับมุมมองที่กำหนดเอง:

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
@ รัลสพูนใช่มันใช้งานได้สำหรับ Fragment และ Custom View โปรดตรวจสอบรหัสตัวอย่าง ฉันแก้ไขคำตอบของฉัน ฉันแนะนำให้คุณไปที่เอกสารอย่างเป็นทางการที่นี่github.com/frankiesardo/icepickเพื่อค้นหาตัวอย่างโค้ดเพิ่มเติม
THANN Phearum

@ChetanMehra คุณหมายถึงคลาสมุมมองที่กำหนดเองใช่มั้ย หากเป็นมุมมองที่กำหนดเองเราสามารถแทนที่ onSaveInstanceState และ onRestoreInstanceState เช่นตัวอย่างข้างต้นของ CustomView
THANN Phearum

ฉันหมายถึงคลาสวัตถุภายในคลาสมุมมองตัวอย่างเช่นคลาส CustomView ขยายมุมมอง {
Chetan Mehra

@THANNPhearum ฉันควรถามเป็นคำถามอื่นหรือไม่
Chetan Mehra

ฉันเห็น. ถ้าเป็นเช่นนั้น ClassA ของคุณควรเป็นพัสดุ ตามที่ได้กล่าวไปแล้วว่ามันใช้งานได้สำหรับกิจกรรมชิ้นส่วนหรือวัตถุใด ๆ ที่จำเป็นต้องทำให้สถานะเป็นอนุกรมบนบันเดิล
THANN Phearum

6

ไม่แน่ใจว่าโซลูชันของฉันขมวดคิ้วหรือไม่ แต่ฉันใช้บริการที่ถูกผูกไว้เพื่อยืนยันสถานะ ViewModel ไม่ว่าคุณจะเก็บไว้ในหน่วยความจำในบริการหรือยังคงมีอยู่และดึงข้อมูลจากฐานข้อมูล SQLite ขึ้นอยู่กับความต้องการของคุณ นี่คือสิ่งที่บริการของรสชาติใด ๆ ที่พวกเขาให้บริการเช่นการรักษาสถานะการใช้งานและตรรกะทางธุรกิจทั่วไปที่เป็นนามธรรม

เนื่องจากข้อ จำกัด ของหน่วยความจำและการประมวลผลที่มีอยู่ในอุปกรณ์พกพาฉันจึงปฏิบัติต่อมุมมอง Android ในลักษณะเดียวกันกับหน้าเว็บ หน้าไม่ได้รักษาสถานะมันเป็นส่วนประกอบของเลเยอร์การนำเสนอที่มีวัตถุประสงค์เพียงเพื่อนำเสนอสถานะแอปพลิเคชันและยอมรับอินพุตของผู้ใช้เท่านั้น แนวโน้มล่าสุดในสถาปัตยกรรมเว็บแอปใช้การใช้รูปแบบโมเดลอายุมุมมองคอนโทรลเลอร์ (MVC) ที่หน้าเป็นมุมมองข้อมูลโดเมนคือรูปแบบและตัวควบคุมอยู่ด้านหลังบริการเว็บ รูปแบบเดียวกันสามารถใช้กับ Android ด้วยมุมมองความเป็นอยู่ ... มุมมองรูปแบบคือข้อมูลโดเมนของคุณและคอนโทรลเลอร์ถูกใช้งานเป็นบริการที่เชื่อมโยงกับ Android เมื่อใดก็ตามที่คุณต้องการให้มุมมองโต้ตอบกับคอนโทรลเลอร์ให้ผูกไว้กับ start / resume และ unbind เมื่อ stop / pause

วิธีการนี้จะให้โบนัสเพิ่มเติมแก่คุณในการบังคับใช้หลักการแยกการออกแบบที่เกี่ยวข้องซึ่งคุณสามารถย้ายตรรกะทางธุรกิจแอปพลิเคชันไปยังบริการของคุณซึ่งจะช่วยลดตรรกะซ้ำซ้อนในหลายมุมมองและช่วยให้มุมมอง


5

Kotlin

คุณต้องแทนที่onSaveInstanceStateและonRestoreInstanceStateจัดเก็บและเรียกใช้ตัวแปรที่คุณต้องการให้คงอยู่

กราฟวงจรชีวิต

เก็บตัวแปร

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

ดึงตัวแปร

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 ให้ViewModelsสำหรับการบันทึกสถานะคุณควรลองใช้มันแทน saveInstanceState


3
นี่ไม่เป็นความจริง. จากเอกสารประกอบ: "ไม่เหมือนกับสถานะอินสแตนซ์ที่บันทึกไว้ ViewModels จะถูกทำลายในระหว่างกระบวนการที่เริ่มต้นโดยระบบนี่คือเหตุผลที่คุณควรใช้วัตถุ ViewModel ร่วมกับ onSaveInstanceState () (หรือการมีอยู่ของดิสก์อื่น ๆ ), stashing identifier ใน นางแบบรีโหลดข้อมูลหลังจากระบบตาย "
Vyacheslav Martynenko

เพิ่งพบปัญหานี้โดยการอนุญาตให้เปลี่ยนเป็นพื้นหลัง
Brill Pappin

ฉันเห็นด้วยจากเอกสาร "หากคุณต้องการจัดการกระบวนการที่เริ่มโดยระบบคุณอาจต้องการใช้ onSaveInstanceState () เป็นข้อมูลสำรอง"
Zhar

2

มีวิธีที่จะทำให้ Android บันทึกสถานะโดยไม่ต้องใช้วิธีการใด ๆ เพียงเพิ่มบรรทัดนี้ในรายการของคุณในการประกาศกิจกรรม:

android:configChanges="orientation|screenSize"

ควรมีลักษณะเช่นนี้:

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

ที่นี่คุณสามารถหาข้อมูลเพิ่มเติมเกี่ยวกับคุณสมบัตินี้

ขอแนะนำให้ Android จัดการสิ่งนี้ให้คุณมากกว่าการจัดการด้วยตนเอง


2
สิ่งนี้ไม่เกี่ยวกับการบันทึกสถานะคุณเพียงแค่ยกเลิกการปรับทิศทางโปรดจำไว้ว่าคุณสามารถเริ่มต้นแอปและหยุดชั่วคราวและเริ่มต้นใหม่ได้ตลอดเวลาสำหรับกิจกรรมที่แตกต่าง
lord-ralf-adolf

1
คำตอบนี้สำหรับผู้ที่ต้องการบันทึกรัฐเมื่อการปฐมนิเทศมีการเปลี่ยนแปลงและต้องการหลีกเลี่ยงความเข้าใจและการใช้งานที่ซับซ้อน
IgniteCoders

ยุติธรรมพอฉันเห็นประเด็นของคุณฉันคิดว่าคนส่วนใหญ่ที่ดิ้นรนเพื่อบันทึกการใช้ชิ้นส่วนเพราะกิจกรรมจริง ๆ บันทึกสถิติขององค์ประกอบ UI ตราบเท่าที่พวกเขามี ID แต่ชิ้นส่วนมีความพิเศษมากขึ้นฉันใช้ชิ้นส่วนเพียงครั้งเดียว พวกเขาอีกครั้งสถิติของอินสแตนซ์ที่บันทึกไว้นั้นเป็นความเจ็บปวดที่ต้องจัดการกับ
ลอร์ด - ราล์ฟ - อดอล์ฟ

มันใช้งานได้ ... ขอบคุณ
Fanadez

1

สิ่งที่จะบันทึกและสิ่งที่จะไม่?

เคยสงสัยไหมว่าทำไมข้อความในไฟล์นั้นถึงEditTextถูกบันทึกโดยอัตโนมัติในขณะที่การวางแนวนั้นเปลี่ยนไป? คำตอบนี้เหมาะสำหรับคุณ

เมื่ออินสแตนซ์ของกิจกรรมถูกทำลายและระบบจะสร้างอินสแตนซ์ใหม่ (ตัวอย่างเช่นการเปลี่ยนแปลงการกำหนดค่า) พยายามสร้างใหม่โดยใช้ชุดข้อมูลที่บันทึกของสถานะกิจกรรมเก่า ( สถานะอินสแตนซ์ )

สถานะอินสแตนซ์คือชุดของคู่ของคีย์ - ค่าที่เก็บไว้ในBundleวัตถุ

ตามค่าเริ่มต้นระบบจะบันทึกวัตถุมุมมองในชุดรวมตัวอย่างเช่น

  • ข้อความใน EditText
  • เลื่อนตำแหน่งในListView, ฯลฯ

หากคุณต้องการตัวแปรอื่นที่จะบันทึกเป็นส่วนหนึ่งของสถานะอินสแตนซ์คุณควรเอาชนะ onSavedInstanceState(Bundle savedinstaneState)ใช้วิธี

ตัวอย่างเช่น, int currentScoreใน GameActivity

รายละเอียดเพิ่มเติมเกี่ยวกับ onSavedInstanceState (บันเดิลที่บันทึกไว้ใน stane) ในขณะที่บันทึกข้อมูล

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

ดังนั้นโดยไม่ได้ตั้งใจถ้าคุณลืมที่จะเรียก super.onSaveInstanceState(savedInstanceState);พฤติกรรมเริ่มต้นจะไม่ทำงานเช่นข้อความใน EditText จะไม่ถูกบันทึก

ตัวเลือกใดสำหรับการกู้คืนสถานะกิจกรรม

 onCreate(Bundle savedInstanceState)

หรือ

onRestoreInstanceState(Bundle savedInstanceState)

ทั้งสองวิธีรับวัตถุ Bundle เดียวกันดังนั้นจึงไม่สำคัญว่าคุณจะเขียนตรรกะการกู้คืนที่ใด ข้อแตกต่างคือในonCreate(Bundle savedInstanceState)วิธีที่คุณจะต้องให้การตรวจสอบเป็นโมฆะในขณะที่มันไม่จำเป็นในกรณีหลัง คำตอบอื่น ๆ มีตัวอย่างโค้ดอยู่แล้ว คุณสามารถอ้างอิงได้

รายละเอียดเพิ่มเติมเกี่ยวกับ onRestoreInstanceState (บันเดิลที่บันทึกไว้ใน staneState)

@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);เพื่อให้ระบบกู้คืนมุมมองลำดับชั้นตามค่าเริ่มต้น

โบนัส

ระบบonSaveInstanceState(Bundle savedInstanceState)ถูกเรียกใช้เมื่อผู้ใช้ตั้งใจที่จะกลับมาที่กิจกรรม ตัวอย่างเช่นคุณใช้แอพ X และคุณได้รับสาย คุณย้ายไปที่แอพผู้โทรและกลับมาที่แอพ X ในกรณีนี้onSaveInstanceState(Bundle savedInstanceState)วิธีการจะถูกเรียกใช้

แต่ให้พิจารณาสิ่งนี้หากผู้ใช้กดปุ่มย้อนกลับ สันนิษฐานว่าผู้ใช้ไม่ได้ตั้งใจที่จะกลับมาที่กิจกรรมดังนั้นในกรณีนี้ระบบonSaveInstanceState(Bundle savedInstanceState)จะไม่ถูกเรียกใช้ คุณควรพิจารณาถึงสถานการณ์ทั้งหมดในขณะที่กำลังบันทึกข้อมูล

ลิงค์ที่เกี่ยวข้อง:

การสาธิตการทำงานเริ่มต้นเอกสาร
Android อย่างเป็นทางการ


1

ตอนนี้มันสมเหตุสมผลแล้วที่จะทำสองวิธีในโมเดลการดู หากคุณต้องการบันทึกครั้งแรกเป็นอินสแตนซ์ที่บันทึกไว้: คุณสามารถเพิ่มพารามิเตอร์สถานะในโมเดลมุมมองเช่น https://developer.android.com/topic/l ไลบรารี/architecture/viewmodel-savedstate#java

หรือคุณสามารถบันทึกตัวแปรหรือวัตถุในโมเดลการดูในกรณีนี้โมเดลการดูจะเก็บวงจรชีวิตจนกว่ากิจกรรมจะถูกทำลาย

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

คุณพูดถูก แต่คลังนี้ยังคงเปิดตัวอยู่ดังนั้นฉันคิดว่าเราควรรอ ...
Zhar

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.