저장 인스턴스 상태를 사용하여 안드로이드 활동 상태를 저장하는 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/151777

문제

나는 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 분 정도 찌르고 있었고 명백한 것을 찾지 못했습니다.

도움이 되었습니까?

해결책

재정의해야합니다 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");
}

일반적 으로이 기술을 사용하여 응용 프로그램의 인스턴스 값 (선택, 구원받지 않은 텍스트 등)을 저장합니다.

다른 팁

그만큼 savedInstanceState 현재 항해 또는 선택 정보와 같은 현재 활동 인스턴스와 관련된 상태를 저장하기위한 것입니다. 따라서 Android가 활동을 파괴하고 재현하면 이전과 마찬가지로 다시 돌아올 수 있습니다. 문서를 참조하십시오 onCreate 그리고 onSaveInstanceState

더 오래 살아있는 상태의 경우 SQLITE 데이터베이스, 파일 또는 기본 설정을 사용하는 것을 고려하십시오. 보다 지속적인 상태를 절약합니다.

그것은 그렇습니다 아니다 사용하기에 안전합니다 onSaveInstanceState 그리고 onRestoreInstanceState 영구 데이터의 경우, 활동 상태에 대한 문서에 따르면 http://developer.android.com/reference/android/app/activity.html.

문서는 ( '활동 수명주기'섹션에서)를 나타냅니다.

영구 데이터를 저장하는 것이 중요합니다. onPause() 대신에 onSaveInstanceState(Bundle)나중에는 수명주기 콜백의 일부가 아니기 때문에 문서에 설명 된대로 모든 상황에서 호출되지는 않습니다.

즉, 영구 데이터에 대한 저장/복원 코드를 onPause() 그리고 onResume()!

편집하다: 추가 설명을 위해 다음은 다음과 같습니다 onSaveInstanceState() 선적 서류 비치:

이 방법은 활동이 죽기 전에 호출되어 미래에 일정 시간이되면 상태를 복원 할 수 있습니다. 예를 들어, 활동 B가 활동 A 앞에서 시작되고 어느 시점에서 A a가 자원을 되 찾기 위해 죽인 경우, 활동 A는 사용자가 반환 할 때이 방법을 통해 사용자 인터페이스의 현재 상태를 저장할 수 있습니다. Activity A에 사용자 인터페이스의 상태는 onCreate(Bundle) 또는 onRestoreInstanceState(Bundle).

내 동료는 활동 라이프 사이클 및 상태 정보에 대한 설명, 상태 정보를 저장하는 방법, 주에 저축을 포함하여 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();
}

이것은 Android 개발의 고전적인 'Gotcha'입니다. 여기에는 두 가지 문제가 있습니다.

  • 최소한 레거시 버전에서 개발 중에 애플리케이션 스택 관리를 크게 복잡하게하는 미묘한 안드로이드 프레임 워크 버그가 있습니다 (완전히/언제 고정되었는지는 확실하지 않음). 아래 버그에 대해 논의하겠습니다.
  • 이 문제를 관리하는 '정상'또는 의도 된 방법은 그 자체로 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

기본적으로, 싱글 스크래그 플래그와 함께 응용 프로그램이 시작된 다음 나중에 홈 화면 또는 런처 메뉴에서 시작하면 그 후속 호출이 새로운 작업을 만들 것입니다 ... 앱의 두 가지 인스턴스가 효과적으로 있습니다. 같은 스택에 거주하는 ... 매우 이상한 점이 매우 빨라집니다. 이것은 개발 중 앱 (예 : Eclipse 또는 Intellij)을 시작할 때 발생하는 것 같습니다. 또한 App Store 업데이트 메커니즘 중 일부를 통해 (따라서 사용자에게도 영향을 미칩니다).

나는 나의 주요 문제가 의도 된 프레임 워크 동작이 아니라이 버그라는 것을 깨달기 전에 몇 시간 동안이 스레드를 통해 싸웠다. 훌륭한 글과 해결 방법 (업데이트 : 아래 참조)이 답변에서 @kaciula 사용자의 것 같습니다.

홈 키 프레스 동작

2013 년 6 월 업데이트: 몇 달 후, 나는 마침내 '올바른'솔루션을 찾았습니다. Stateful StartApp 플래그를 직접 관리 할 필요는 없으며 프레임 워크에서이를 감지하고 적절하게 보석금을 내릴 수 있습니다. 나는 이것을 발사 능력의 시작 부분 근처에서 사용합니다.

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 시스템이 메모리가 필요하고 응용 프로그램을 죽일 때 호출됩니다. 사용자가 응용 프로그램을 닫을 때 호출되지 않습니다. 그래서 응용 프로그램 상태도 저장해야한다고 생각합니다. onPause 그것은 지속적인 스토리지에 저장해야합니다 Preferences 또는 Sqlite

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

  1. 사용자는 애플리케이션을 종료하고 나중에 응용 프로그램을 다시 열지 만 애플리케이션은 마지막 세션에서 데이터를 다시로드해야합니다. 따라서 SQLITE 사용과 같은 지속적인 스토리지 접근법이 필요합니다.
  2. 사용자는 응용 프로그램을 전환 한 다음 원본으로 돌아온 다음 꺼진 곳에서 픽업하려고합니다. onSaveInstanceState() 그리고 onRestoreInstanceState() 일반적으로 적절합니다.

상태 데이터를 지속적으로 저장하면 onResume() 또는 onCreate() (또는 실제로 수명주기 호출에). 이것은 원하는 행동 일 수도 있고 아닐 수도 있습니다. 당신이 그것을 묶음에 보관하는 경우 InstanceState, 그것은 일시적이며 동일한 사용자 '세션'에 사용하기 위해 데이터를 저장하는 데만 적합하지만 (세션이 느슨하게 사용됨) '세션'사이에는 그렇지 않습니다.

한 가지 접근 방식이 다른 것보다 더 낫지는 않습니다.

저축 상태는 내가 염려하는 한 기껏해야 Kludge입니다. 영구 데이터를 저장 해야하는 경우 sqlite 데이터 베이스. 안드로이드가 그것을 만듭니다 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;

나는 대답을 찾았다 고 생각합니다. 간단한 말로 무엇을했는지 말해 드리겠습니다.

Activity1과 Activity2의 두 가지 활동이 있다고 가정하고 활동 1에서 Activity2로 탐색하고 (Activity2에서 일부 작업을 수행 했음) Activity1의 버튼을 클릭하여 Activity 1으로 다시 돌아 왔습니다. 이제이 단계에서 나는 Activity2로 돌아가고 싶었고 왼쪽 왼쪽 활동 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);

그리고 activity2 on 버튼 클릭 이벤트에서 다음과 같이했습니다.

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

이제 우리가 Activity2에서 변경 한 내용이 무엇이든 상실되지 않을 것이며, 이전에 떠난 것과 동일한 상태에서 활동 2를 볼 수 있다는 것입니다.

나는 이것이 답이라고 믿고 이것은 나에게 잘 작동합니다. 내가 틀렸다면 나를 수정하십시오.

onSaveInstanceState() 과도 데이터의 경우 (복원 onCreate()/onRestoreInstanceState()), onPause() 영구 데이터의 경우 (복원 onResume()). Android 기술 리소스에서 :

onsaveinstancestate () 활동이 중단되고 재개되기 전에 죽일 수있는 경우 Android에 의해 호출됩니다! 이는 활동이 다시 시작될 때 동일한 조건으로 재직하는 데 필요한 상태를 저장해야 함을 의미합니다. 그것은 oncreate () 메소드의 대응 물이며, 실제로 oncreate ()로 전달 된 SavedInstancestate 번들은 onsaveinstancestate () 메소드에서 Outstate와 제작하는 것과 동일한 번들입니다.

onpause () 그리고 onresume () 또한 무료 방법입니다. onpause ()는 (예를 들어 마감) (예를 들어)를 유도하더라도 활동이 종료 될 때 항상 호출됩니다. 이를 사용하여 현재 메모를 데이터베이스에 다시 저장합니다. 모범 사례는 수동 상태에있을 때 onpause () 중에 릴리스 할 수있는 리소스를 릴리스하여 자원을 덜 차지하는 것입니다.

진짜 onSaveInstance 활동이 배경으로 갈 때 콜렌

문서의 인용 : "방법 onSaveInstanceState(Bundle) 이러한 배경 상태에 활동을 배치하기 전에 호출됩니다. "

보일러 플레이트를 줄이기 위해 다음을 사용합니다 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 이에 따라 라이센스가 부여됩니다 MIT 라이센스.

한편 나는 일반적으로 더 이상 사용하지 않습니다

Bundle savedInstanceState & Co

수명주기는 대부분의 활동이 너무 복잡하고 필요하지 않습니다.

그리고 Google은 그 자체로 신뢰할 수 없습니다.

내 방식은 선호도에서 즉시 변경 사항을 저장하는 것입니다.

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

어떤 방식 으로든 공동 예약은 번들과 비슷하게 작동합니다. 그리고 자연스럽게 그리고 처음에는 그러한 값을 선호도에서 읽어야합니다.

복잡한 데이터의 경우 환경 설정을 사용하는 대신 SQLITE를 사용할 수 있습니다.

이 개념을 적용 할 때, 활동은 후면 스택으로 인한 재부팅 또는 다시 열 수있는 재부팅으로 초기 오픈인지 여부에 관계없이 마지막 저장된 상태를 계속 사용합니다.

원래 질문에 직접 대답합니다. SavedInstancestate는 활동이 다시 만들어지지 않기 때문에 무효입니다.

귀하의 활동은 다음과 같은 경우 상태 번들로 만 다시 제작됩니다.

  • 새 활동 인스턴스가 생성 될 수있는 방향 또는 전화 언어 변경과 같은 구성 변경.
  • OS가 활동을 파괴 한 후 배경에서 앱으로 돌아갑니다.

안드로이드는 메모리 압력 아래 또는 오랜 시간 동안 배경에 있었을 때 배경 활동을 파괴합니다.

Hello World 예제를 테스트 할 때는 활동으로 떠나고 복귀하는 몇 가지 방법이 있습니다.

  • 뒤로 버튼을 누르면 활동이 완료되었습니다. 앱을 다시 시작하는 것은 새로운 인스턴스입니다. 당신은 배경에서 전혀 재개하지 않습니다.
  • 홈 버튼을 누르거나 작업 스위처를 사용하면 활동이 백그라운드로 들어갑니다. 응용 프로그램으로 다시 탐색 할 때 OnCreate는 활동을 파괴 해야하는 경우에만 호출됩니다.

대부분의 경우 집에 눌러 앱을 다시 시작하는 경우 활동을 재창조 할 필요가 없습니다. 이미 메모리에 존재하므로 oncreate ()가 호출되지 않습니다.

설정 아래에는 옵션이 있습니다 -> "활동을 유지하지 말라"라는 개발자 옵션이 있습니다. 활성화되면 Android는 항상 활동을 파괴하고 배경이 될 때 재현합니다. 이는 최악의 시나리오를 시뮬레이션하기 때문에 개발할 때 활성화 된 퇴직 옵션입니다. (활동을 항상 재활용하는 메모리 장치가 낮습니다).

다른 답변은 그들이 당신에게 주를 저장하는 올바른 방법을 가르쳐주는 것에서 가치가 있지만, 나는 그들이 당신의 코드가 당신이 기대했던 방식으로 작동하지 않는지에 대한 대답을 실제로 느끼지 못했다고 생각했습니다.

그만큼 onSaveInstanceState(bundle) 그리고 onRestoreInstanceState(bundle) 메소드는 화면을 회전하는 동안 단순히 데이터 지속성에 유용합니다 (방향 변경).
응용 프로그램간에 전환하는 동안 좋지 않습니다 (이후로 onSaveInstanceState() 방법은하지만 onCreate(bundle) 그리고 onRestoreInstanceState(bundle) 다시 호출되지 않습니다.
더 지속성을 위해 공유 환경 설정을 사용하십시오. 이 기사를 읽으십시오

내 문제는 애플리케이션 수명 동안 만 지속성이 필요하다는 것입니다 (즉, 동일한 앱 내에서 다른 하위 활성화를 시작하고 장치를 회전하는 등 단일 실행). 위의 답변의 다양한 조합을 시도했지만 모든 상황에서 원하는 것을 얻지 못했습니다. 결국 저를 위해 일한 것은 onecreate 동안 SavedInstancestate에 대한 언급을 얻는 것이 었습니다.

mySavedInstanceState=savedInstanceState;

그리고 필요할 때 다음을 따라 변수의 내용을 얻으려면 다음을 사용하십시오.

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

나는 사용한다 onSaveInstanceState그리고 onRestoreInstanceState 위에서 제안한대로, 나는 또한 변수가 변경 될 때 (예 : 사용 putBoolean)

허용 된 답변이 정확하지만,이 라이브러리를 사용하여 Android에서 활동 상태를 저장하는 더 빠르고 쉬운 방법이 있습니다. 얼음 깨는 송곳. 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.

활동이 만들어지면 oncreate () 메소드가 호출됩니다.

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

SavedInstancestate는 처음으로 Null이지만 번들 클래스의 대상이지만 재현 할 때 값이 포함되어 있습니다. Activity의 상태를 저장하려면 onsaveinstancestate ()를 무시해야합니다.

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

Outstate.putString ( "Key", "Welcome Back")과 같은 "Outstate"번들 객체에 값을 넣고 Super를 호출하여 저장하십시오. 활동이 파괴되면 상태가 번들 객체에 저장되고 OnCreate () 또는 OnRestoreInstancestate ()에서 레크리에이션 후 복원 될 수 있습니다. 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");
    }

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

  1. 사용 onSaveInstanceState() 그리고 onRestoreInstanceState().
  2. 매니페스트 android:configChanges="orientation|screenSize".

두 번째 방법을 사용하는 것이 좋습니다. 내 경험 중 하나에서는 초상화에서 풍경으로 회전하는 동안 장치 화면의 절반을 검은 색으로 만들었습니다.

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

예 : JSON 객체를 지속하려면 사례를 고려하십시오. getters and 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); 

}

다음은 다음과 같습니다 스티브 모슬리대답 (by ToolMakersteve) 사물을 원근법으로 만듭니다 (전체적으로 onsaveinstancestate vs onpause, East Cost vs West Cost Saga)

@vvk- 나는 부분적으로 동의하지 않습니다. 앱을 종료하는 몇 가지 방법은 aSaveInstancestate (ISOS)를 유발하지 않습니다. 이것은 ISOS의 유용성을 제한합니다. 최소한의 OS 리소스를 위해 지원할 가치가 있지만 앱이 앱이 어떻게 종료되었는지에 관계없이 사용자를 상태로 반환하려면 대신 지속적인 스토리지 접근 방식을 사용해야합니다. oncreate를 사용하여 번들을 확인하고 누락 된 경우 확인합니다. 지속적인 저장. 이것은 의사 결정을 중앙 집중화합니다. 충돌 또는 뒤로 버튼 종료 또는 사용자 정의 메뉴 항목 종료에서 복구하거나 며칠 후 스크린 사용자로 돌아갈 수 있습니다. - Toolmakersteve 9 월 19 일 10시 38 분

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

옵션이 없으면 기본값 추가

활동 상태 데이터가 저장됩니다 onCreate(), 먼저 재정의하여 SavedInstancestate에 데이터를 저장해야합니다. SaveInstanceState(Bundle savedInstanceState) 방법.

활동이 파괴 될 때 SaveInstanceState(Bundle savedInstanceState) 메소드가 호출되고 저장하려는 데이터를 저장합니다. 그리고 당신은 똑같습니다 onCreate() 활동이 다시 시작될 때.

이 문제를 해결하는 간단한 빠른 문제가 사용 중입니다 얼음 깨는 송곳

먼저 라이브러리를 설정하십시오 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);
  }
}

그것은 묶음에서 상태를 직렬화 해야하는 활동, 조각 또는 객체에 적합합니다 (예 : Mortar의 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
}

내 솔루션이 눈살을 찌푸 렸는지 여부는 확실하지 않지만 뷰 모델 상태를 유지하기 위해 바운드 서비스를 사용합니다. 서비스에 메모리에 저장하든 SQLITE 데이터베이스에서 검색할지 여부는 요구 사항에 따라 다릅니다. 이것이 모든 맛의 서비스가하는 일이며, 응용 프로그램 상태 유지 및 추상적 인 공통 비즈니스 논리와 같은 서비스를 제공합니다.

모바일 장치에 내재 된 메모리 및 처리 제약으로 인해 웹 페이지와 유사한 방식으로 Android보기를 처리합니다. 이 페이지는 상태를 유지하지 않으며, 순전히 응용 프로그램 상태를 제시하고 사용자 입력을 수락하는 것이 목적이있는 프레젠테이션 계층 구성 요소입니다. Web App Architecture의 최근 트렌드는 오래된 모델, View, Controller (MVC) 패턴을 사용하는 데 사용됩니다. 여기서 페이지는보기이며 도메인 데이터는 모델이며 컨트롤러는 웹 서비스 뒤에 있습니다. 뷰가 있는데, 뷰, 모델은 도메인 데이터이며 컨트롤러는 Android 바운드 서비스로 구현됩니다. 컨트롤러와 상호 작용하기를 원할 때마다 시작/이력서에 묶고 정지/일시 중지에서 풀어주십시오.

이 접근법은 모든 애플리케이션 비즈니스 로직을 서비스로 이동하여 여러 관점에서 중복 된 논리를 줄이고 또 다른 중요한 설계 원칙, 단일 책임을 시행 할 수 있다는 점에서 관심 설계 원칙의 분리를 시행하는 추가 보너스를 제공합니다.

이제 Android가 제공합니다 뷰 모델 상태를 저장하려면 SaveInstancestate 대신 사용해야합니다.

무엇을 저장하고 무엇을하지 않습니까?

왜 텍스트가 EditText 방향이 변경되는 동안 자동으로 저장됩니까? 글쎄,이 대답은 당신을위한 것입니다.

활동 인스턴스가 파괴되고 시스템이 새 인스턴스 (예 : 구성 변경)를 재현 할 때. 구식 활동 상태의 저장된 데이터 세트를 사용하여 그것을 재현하려고합니다 (인스턴스 상태).

인스턴스 상태는의 모음입니다 핵심 가치 a에 저장된 쌍 Bundle 물체.

기본적으로 시스템은 예를 들어 번들에 뷰 객체를 저장합니다.

  • 텍스트를 입력합니다 EditText
  • a에서 스크롤 위치 ListView, 등.

예를 들어 상태의 일부로 저장하려면 다른 변수가 필요한 경우 우세하다 onSavedInstanceState(Bundle savedinstaneState) 방법.

예를 들어, int currentScore Gameactivity에서

데이터를 저장하는 동안 avedinstancestate (Bundle SavedInstanestate)에 대한 자세한 내용

@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에서 IE 텍스트가 작동하지 않습니다.

활동 상태를 복원하기 위해 어느 것을 선택해야합니까?

 onCreate(Bundle savedInstanceState)

또는

onRestoreInstanceState(Bundle savedInstanceState)

두 방법 모두 동일한 번들 객체를 얻으므로 복원 로직을 작성하는 곳은 중요하지 않습니다. 유일한 차이점은 onCreate(Bundle savedInstanceState) 방법 후자의 경우에는 필요하지 않은 상태에서 무효 점검을해야합니다. 다른 답변에는 이미 코드 스 니펫이 있습니다. 당신은 그들을 참조 할 수 있습니다.

OnRestoreInstancestate에 대한 자세한 내용 (Bundle SavedInstanestate)

@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); 시스템이 기본적으로 View Hierarchy를 복원합니다

보너스

그만큼 onSaveInstanceState(Bundle savedInstanceState) 사용자가 활동으로 돌아 오려고 할 때만 시스템에 의해 호출됩니다. 예를 들어, App X를 사용하고 갑자기 전화를받습니다. 발신자 앱으로 이동하여 앱 X로 돌아옵니다.이 경우 onSaveInstanceState(Bundle savedInstanceState) 메소드가 호출됩니다.

그러나 사용자가 뒤로 버튼을 누르면 이것을 고려하십시오. 사용자가 활동으로 돌아 오려고하지 않는다고 가정 하므로이 경우 onSaveInstanceState(Bundle savedInstanceState) 시스템에 의해 호출되지 않습니다. 데이터를 저장하면서 모든 시나리오를 고려해야한다는 점입니다.

관련 링크 :

기본 동작의 데모
안드로이드 공식 문서.

코 틀린

당신은 무시해야합니다 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
}

더 나은 아이디어가 있습니다. 이것은 OnCreate를 다시 호출하지 않고 데이터를 저장하는 것이 좋습니다. 오리엔테이션이 변경 될 때 활동에서 비활성화 할 수 있습니다.

당신의 매니페스트에서 :

<activity android:name=".MainActivity"
        android:configChanges="orientation|screenSize">

편집하다:이 솔루션은 메모리가 낮아서 Android가 프로세스를 죽일 때 작동하지 않습니다. 데이터를 확실히 유지 해야하는 경우이 경우 SavedInstancestate를 사용해야합니다. 그렇지 않으면이 방법을 사용하는 것이 좋습니다. 시원하고 사용하기 쉽습니다!

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top