android - 저장 - 안드로이드 생명주기 활용



인스턴스 상태 저장을 사용하여 Android 액티비티 상태 저장하기 (18)

프로젝트에 LiveData (Android Architecture Components) 추가하기

다음 종속성을 추가하십시오.

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

LiveData는 옵저버를 가져와 STARTED 또는 RESUMED 상태 일 때만 데이터 변경 사항에 대해 알립니다. LiveData 의 이점은 활동이 STARTED 또는 RESUMED 가 아닌 다른 상태 가되면 관찰자에게 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);
        }
    });

}

https://rueisnom.com

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 분 정도의 문서에서 벗어나 뻔한 것을 발견하지 못했다.


Answer #1

Activity가 백그라운드로 이동하면 실제로 onSaveInstance 상태 onSaveInstance

문서에서 인용 : " onSaveInstanceState(Bundle) 메서드는 이러한 백그라운드 상태에 활동을 배치하기 전에 호출됩니다."


Answer #2

동료는 활동주기 및 상태 정보, 상태 정보 저장 방법, State BundleSharedPreferences 저장 방법과 같은 Android 장치의 응용 프로그램 상태를 설명하는 기사를 작성하여 여기에서 살펴보십시오 .

이 기사에서는 세 가지 방법을 다룹니다.

Instance State Bundle을 사용하여 응용 프로그램 수명 (즉, 일시적으로)에 대한 로컬 가변 / 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();
}

Retained Non-Configuration Instance를 사용하여 응용 프로그램 수명 내의 활동간에 메모리에 객체 인스턴스를 유지

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

Answer #3

두 방법 모두 유용하고 유효하며 둘 모두 다른 시나리오에 가장 적합합니다.

  1. 사용자는 응용 프로그램을 종료하고 나중에 다시 열지 만 응용 프로그램은 마지막 세션의 데이터를 다시로드해야합니다. SQLite와 같은 영구 저장소 접근 방식이 필요합니다.
  2. 사용자가 응용 프로그램을 전환 한 다음 원래 위치로 되돌아 와서 중단 한 부분을 선택하려고합니다. onSaveInstanceState()onRestoreInstanceState() 에서 번들 데이터 (응용 프로그램 상태 데이터 등)를 저장하고 복원하는 것이 일반적으로 적합합니다.

상태 데이터를 영구적 인 방식으로 저장하면 onResume() 또는 onCreate() 에서 다시로드 할 수 있습니다 (실제로는 모든 라이프 사이클 호출에서). 이것은 원하는 동작 일 수도 있고 그렇지 않을 수도 있습니다. InstanceState 의 번들에 저장하면 일시적이며 같은 사용자 (세션이라는 용어를 느슨하게 사용)에 사용할 데이터를 저장하는 데만 적합하지만 '세션'간에는 사용할 수 없습니다.

한 가지 방법이 모든 것처럼 다른 것보다 낫지는 않습니다. 필요한 행동을 이해하고 가장 적절한 방법을 선택하는 것이 중요합니다.


Answer #4

일시적인 데이터의 onSaveInstanceState() onCreate() / onRestoreInstanceState() ), onPause() ( onResume() 에서 복원 된 onPause() 영구 데이터. Android 기술 리소스에서 가져온 것 :

onSaveInstanceState () 는 활동이 중지되고 다시 시작되기 전에 종료 될 수있는 경우 Android에서 호출합니다! 즉, 작업이 다시 시작될 때 동일한 조건으로 다시 초기화하는 데 필요한 모든 상태를 저장해야합니다. onCreate () 메서드에 상응하며 실제로 onCreate ()에 전달 된 savedInstanceState Bundle은 onSaveInstanceState () 메서드에서 outState로 생성하는 번들과 같습니다.

onPause ()onResume () 은 또한 무료 메서드입니다. onPause ()는 Activity를 종료 할 때 호출합니다 (예 : finish () 호출과 함께). 우리는 이것을 사용하여 현재 메모를 다시 데이터베이스에 저장합니다. 올바른 방법은 onPause () 도중 해제 할 수있는 모든 리소스를 해제하여 수동 상태 인 경우 리소스를 적게 사용하는 것입니다.


Answer #5

저축 상태는 내가 아는 한 최선을 다합니다. 영구 데이터를 저장해야하는 경우 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;

Answer #6

developer.android.com/reference/android/app/Activity.html 활동 상태에 대한 문서에 따라 영구 데이터에 onSaveInstanceStateonRestoreInstanceState 를 사용하는 것은 안전 하지 않습니다 .

문서에는 '활동주기'섹션에 다음 내용이 나와 있습니다.

나중에 데이터가 라이프 사이클 콜백의 일부가 아니므로 onSaveInstanceState(Bundle) onPause() 대신 onPause() 에 영구 데이터를 저장하는 것이 중요하므로 해당 설명서에 설명 된대로 모든 상황에서 호출되지 않으므로 유의하십시오.

즉, onPause()onResume() 에 영구 데이터에 대한 저장 / 복원 코드를 저장하십시오!

수정 : 더 명확히하기 위해 여기에 onSaveInstanceState() 설명서가 :

이 메소드는 활동이 종료되기 전에 호출되어 나중에 일정 시간 후에 다시 상태가 복원 될 수 있도록합니다. 예를 들어 액티비티 B가 액티비티 A 앞에 실행되고 액티비티 A가 리소스를 회수하기 위해 죽은 경우, 액티비티 A는이 메소드를 통해 사용자 인터페이스의 현재 상태를 저장할 수 있으므로 사용자가 활동 A에 대해 사용자 인터페이스의 상태는 onCreate(Bundle) 또는 onRestoreInstanceState(Bundle) 를 통해 복원 될 수 있습니다.


Answer #7

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

번들은 본질적으로 NVP ( "Name-Value Pair") 맵을 저장하는 방법이며 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");
}

대개이 기술을 사용하여 응용 프로그램의 인스턴스 값 (선택 사항, 저장되지 않은 텍스트 등)을 저장합니다.


Answer #8

savedInstanceState 는 활동의 현재 인스턴스 (예 : 현재 탐색 또는 선택 정보)와 연관된 상태를 저장하기위한 savedInstanceState 뿐이므로 Android가 활동을 파괴하고 다시 작성하면 이전 상태로 돌아갈 수 있습니다. onCreateonSaveInstanceState 대한 설명서를 참조하십시오.

수명이 긴 상태를 유지하려면 SQLite 데이터베이스, 파일 또는 환경 설정 사용을 고려하십시오. 영구 상태 저장을 참조하십시오.


Answer #9

활동 다시 만들기

사용자가 [뒤로] 버튼을 누르거나 finish() 를 호출하여 활동이 자체 파괴를 알리는 것과 같이 정상적인 앱 동작으로 인해 활동이 파괴되는 경우가 있습니다. 시스템이 현재 중지되어 오랜 시간 동안 사용되지 않았거나 포어 그라운드 활동에 더 많은 자원이 필요한 경우 시스템이 메모리를 복구하기 위해 백그라운드 프로세스를 종료해야하는 경우 시스템이 또한 활동을 파괴 할 수 있습니다.

사용자가 뒤로를 누르거나 activity 이 끝났기 때문에 activity 이 파괴되면 해당 Activity 인스턴스가 더 이상 활동이 필요하지 않음을 나타내므로 Activity 인스턴스의 시스템 개념은 영원히 사라집니다. 그러나 시스템이 정상적인 응용 프로그램 동작이 아닌 시스템 제약으로 인해 활동을 파괴하면 실제 Activity 인스턴스가 사라지더라도 시스템은 사용자가 다시 탐색하면 시스템이 새로운 Activity 인스턴스를 만듭니다 활동이 destroyed 되었을 때의 활동 상태를 설명하는 저장된 데이터 세트를 사용하여 활동의 인스턴스. 시스템이 이전 상태를 복원하는 데 사용하는 저장된 데이터를 "인스턴스 상태"라고하며 Bundle 객체에 저장된 키 - 값 쌍의 모음입니다.

활동 상태에 대한 추가 데이터를 저장하려면 onSaveInstanceState () 콜백 메서드를 재정의해야합니다. 사용자가 활동을 떠날 때 시스템에서이 메소드를 호출하고 예기치 않게 활동이 손상된 경우에 저장 될 Bundle 오브젝트를 전달합니다. 시스템이 나중에 활동 인스턴스를 재 작성해야하는 경우, 동일한 Bundle 오브젝트를 onRestoreInstanceState()onCreate() 메소드 모두에 전달합니다.

시스템에서 활동을 중지하기 시작하면 onSaveInstanceState() (1)가 호출되므로 Activity 인스턴스를 다시 작성해야 할 경우에 대비하여 저장할 상태 데이터를 추가로 지정할 수 있습니다. 활동이 삭제되고 동일한 인스턴스를 다시 작성해야하는 경우 시스템은 (1)에서 정의 된 상태 데이터를 onCreate() 메소드 (2)와 onRestoreInstanceState() 메소드 (3)에 전달합니다.

Activity 상태 저장

활동이 중지되기 시작할 때 시스템은 onSaveInstanceState() 호출하여 활동이 키 - 값 쌍의 콜렉션으로 상태 정보를 저장할 수있게합니다. 이 메소드의 기본 구현은 EditText 위젯의 텍스트 또는 ListView 의 스크롤 위치와 같은 활동의 뷰 계층 구조 상태에 대한 정보를 저장합니다.

활동에 대한 추가 상태 정보를 저장하려면 onSaveInstanceState() 구현하고 키 - 값 쌍을 Bundle 객체에 추가해야합니다. 예 :

  static final String STATE_SCORE = "playerScore";
  static final String STATE_LEVEL = "playerLevel";

  @Override
  public void onSaveInstanceState(Bundle savedInstanceState) {
  // Save the user's current game state
  savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
  savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);

  // Always call the superclass so it can save the view hierarchy state
  super.onSaveInstanceState(savedInstanceState);
}

주의 : 기본 구현이 뷰 계층 구조의 상태를 저장할 수 있도록 onSaveInstanceState() 의 수퍼 클래스 구현을 항상 호출하십시오.

Activity 상태 복원

이전에 파기 된 활동을 재 작성하면 시스템이 활동을 전달한 번들에서 저장된 상태를 복구 할 수 있습니다. onCreate()onRestoreInstanceState() 콜백 메서드는 모두 인스턴스 상태 정보가 들어있는 동일한 Bundle 을받습니다.

onCreate() 메서드는 시스템에서 활동의 새 인스턴스를 만들거나 이전 활동을 다시 만들 때 호출되므로 상태 묶음이 읽으려고 시도하기 전에 null인지 확인해야합니다. 그것이 널이면, 시스템은 이전에 파기 된 것을 복원하는 대신에 활동의 새 인스턴스를 작성 중입니다.

예를 들어 onCreate() 에서 상태 데이터를 복원하는 방법은 다음과 같습니다.

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState); // Always call the superclass first

 // Check whether we're recreating a previously destroyed instance
 if (savedInstanceState != null) {
    // Restore value of members from saved state
    mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
    mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
 } else {
    // Probably initialize members with default values for a new instance
 }

 }

onCreate() 중 상태를 복원하는 대신 onRestoreInstanceState() 를 구현하도록 선택할 수 있습니다.이 메서드는 onStart() 메서드 이후에 시스템이 호출합니다. 복원 할 저장 상태가있는 경우에만 onRestoreInstanceState() 호출되므로 번들이 null인지 여부를 확인할 필요가 없습니다.

  public void onRestoreInstanceState(Bundle savedInstanceState) {
  // Always call the superclass so it can restore the view hierarchy
  super.onRestoreInstanceState(savedInstanceState);

  // Restore state members from saved instance
  mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
  mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
}

Answer #10

이 변경을 구현하는 기본적으로 두 가지 방법이 있습니다.

  1. 와를 사용 onSaveInstanceState()하여 onRestoreInstanceState().
  2. 매니페스트에서 android:configChanges="orientation|screenSize".

두 번째 방법을 사용하지 않는 것이 좋습니다. 내 경험 중 하나 때문에 세로에서 가로로 또는 그 반대로 회전하는 동안 장치 화면의 절반이 검게 나옵니다.

위에서 언급 한 첫 번째 방법을 사용하면 방향이 변경되거나 구성 변경이 발생할 때 데이터를 유지할 수 있습니다. savedInstance 상태 객체 내에 모든 유형의 데이터를 저장할 수있는 방법을 알고 있습니다.

예 : Json 객체를 유지하려는 경우를 생각해보십시오. getter 및 setter를 사용하여 모델 클래스를 만듭니다.

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

}

Answer #11

코 틀린 코드 :

구하다:

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

당신이 Optionals을 원하지 않으면 기본값을 추가하십시오.


Answer #12

내 문제는 응용 프로그램 수명 동안 만 지속성이 필요하다는 것이 었습니다 (즉, 동일한 응용 프로그램 내에서 다른 하위 활동을 시작하고 장치를 회전하는 등의 단일 실행). 위의 여러 가지 조합을 시도했지만 모든 상황에서 내가 원하는 것을 얻지 못했습니다. 결국 나를 위해 일한 것은 onCreate 동안 savedInstanceState에 대한 참조를 얻는 것이 었습니다.

mySavedInstanceState=savedInstanceState;

내 변수의 내용을 얻기 위해 필요할 때이를 사용하십시오.

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

내가 사용 onSaveInstanceState하고 onRestoreInstanceState위에서 제시 한대로하지만 나는 또한 또는 내 대안을 변경할 수있는 변수를 저장하려면 내 방법을 사용할 수있을 것 같아요 (예 : 사용 putBoolean)


Answer #13

다음 내가 사용하는 보일러를 줄일 수 있도록 interface하고 classA를 읽기 / 쓰기로 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);
    }

}

참고 : 이 코드는 MIT 라이센스에 따라 라이센스가 부여 된 AndroidAutowire 라는 라이브러리 프로젝트에서 채택되었습니다 .


Answer #14

에 저장된 활동 상태 데이터를 얻으려면 onCreate()우선 SaveInstanceState(Bundle savedInstanceState)메소드 를 재정 의하여 saveInstanceState에 데이터를 저장해야합니다 .

액티비티 destroy SaveInstanceState(Bundle savedInstanceState)메소드가 호출되면 저장하려는 데이터를 저장합니다. 그리고 onCreate()활동이 다시 시작될 때도 마찬가지입니다 . (활동이 파괴되기 전에 일부 데이터를 저장 했으므로 savedInstanceState는 null이되지 않습니다)


Answer #15

여기 에 물건을 원근감있게 올려 놓은 Steve Moseley 의 답변 ( ToolmakerSteve )이 있습니다 (전체 onSaveInstanceState와 onPause, 동쪽 비용 대 서쪽 비용 사가)

@VVK - 나는 부분적으로 동의하지 않습니다. 앱을 종료하는 몇 가지 방법은 onSaveInstanceState (oSIS)를 트리거하지 않습니다. 이것은 oSIS의 유용성을 제한합니다. 최소한의 OS 리소스 만 있으면 지원할만한 가치가 있지만 앱이 사용자가 있던 상태로 사용자를 되돌리려는 경우 앱을 종료 한 방법과 상관없이 영구 저장 방법을 사용해야합니다. 번들을 확인하기 위해 onCreate를 사용하고, 누락 된 경우 영구 저장소 를 확인 합니다. 이것은 의사 결정을 중앙 집중화합니다. 내가 충돌에서 복구하거나 뒤로 버튼 종료 또는 사용자 정의 메뉴 항목 Exit를하거나 며칠 후에 사용자 화면으로 돌아갈 수 있습니다. - ToolmakerSteve 9 월 19, 15 : 10 : 38


Answer #16

onSaveInstanceState(bundle)onRestoreInstanceState(bundle)메서드는 화면을 회전하는 동안 (오리 엔테이션 변경) 데이터 지속성에 유용합니다.
그들은 이후 (응용 프로그램 사이를 전환하는 동안도 잘되지 않습니다 onSaveInstanceState()메서드가 호출 되나 onCreate(bundle)하고 onRestoreInstanceState(bundle)다시 호출되지 않습니다.
더 지속성 사용 공유 설정하십시오. 이 기사를 읽고


Answer #17

이제 안드로이드는 상태 저장을 위해 ViewModels 을 제공 하므로 saveInstanceState 대신에 그것을 사용하려고 시도해야합니다.





application-state