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


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
savedInstanceState == null은 언제 null이 아닌가?
Trojan.ZBOT

90
말한 것처럼 뒤로 누르는 등의 활동을 통해 명시 적으로 활동을 파괴하고 있습니다. 실제로이 'savedInstanceState'가 사용되는 시나리오는 Android가 레크리에이션을 위해 활동을 파괴하는 경우입니다. 강의 : 활동이 실행되는 동안 휴대 전화의 언어를 변경하는 경우 (따라서 프로젝트와 다른 리소스를로드해야 함) 또 다른 매우 일반적인 시나리오는 휴대폰을 측면으로 회전하여 활동이 다시 생성되고 가로로 표시되는 경우입니다.
villoren

16
두 번째 메시지를 받으려면 dev 옵션에서 "활동을 유지하지 마십시오"를 활성화하십시오. 홈 버튼을 누르고 최근 통화에서 돌아옵니다.
Yaroslav Mytkalyk


6
onSaveInstanceState (번들 savedInstanceState) : 당신은 그것을 할 수있는
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.
}

번들은 기본적으로 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이 아닌 savedInstanceState를 얻을 수없는 것 같습니다.
Adam Jack

491
주의 : 번들에 값을 추가하기 전에 super.onSaveInstanceState (savedInstanceState)를 호출해야합니다. 그렇지 않으면 해당 호출에서 값이 지워집니다 (Droid X Android 2.2).
jkschneider

121
주의 : 공식 문서에 따르면 onsaveinstance 방법이 안드로이드 수명주기의 일부가 아니기 때문에 onPause-Method 내에서 중요한 정보를 저장해야한다고 명시되어 있습니다. developer.android.com/reference/android/app/Activity.html
schlingel 2012 년

32
그 사실 onSaveInstanceState은 화면 방향 변경의 경우를 제외하고 는 거의 쓸모가 없습니다. 거의 모든 경우에 의존 할 수 없으며 UI 상태를 다른 곳에 수동으로 저장해야합니다. 또는 BACK 버튼 동작을 재정 의하여 앱이 종료되는 것을 방지합니다. 그들이 왜 이런 식으로 구현했는지 이해가되지 않습니다. 완전히 직관적이지 않습니다. 그리고이 특별한 방법을 제외하고는 시스템이 물건을 저장하도록주는 번들을 가질 수 없습니다.
chakrit

12
번들에 UI 상태를 저장 / 복원하는 것은 ID가 할당 된 s대해 자동으로 처리 됩니다 . 로부터 문서 : "기본 구현이 호출하여 당신을위한 UI 인스턴스 당 국가의 대부분을 담당 , ID를 가지고있는 계층 구조의 각 뷰와의 ID를 저장하여 현재 복원 모두보기 (집중 ) " 의 기본 구현으로ViewonSaveInstanceStateonSaveInstanceState()onRestoreInstanceState(Bundle)
Vicky Chijwani

433

savedInstanceState안드로이드 파괴가 활동을 다시 작성하는 경우가 예전과 같이, 그것은 다시 올 수 그래서, 단지 예를 들어 현재 탐색 또는 선택 정보를 원하시면, 액티비티의 현재 인스턴스와 연결된 상태를 저장하기위한 것입니다. onCreate및 설명서를 참조하십시오onSaveInstanceState

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


3
savedInstanceState == null은 언제 null이 아닌가?
Trojan.ZBOT

6
시스템이 활동의 ​​새 인스턴스를 작성하는 경우 savedInstanceState가 널이고 복원 할 때 널이 아닙니다.
Gabriel Câmara

6
... 시스템 이 언제 새로운 Activity 인스턴스를 작성 해야하는지에 대한 의문을 제기합니다 . 앱을 종료하는 몇 가지 방법은 번들을 만들지 않으므로 새 인스턴스를 만들어야합니다. 이것이 근본적인 문제입니다. 즉 , 번들의 존재에 의존 할 수 없으며 영구 저장소의 대체 수단을 사용해야합니다. onSave / onRestoreInstanceState의 장점은 시스템 리소스를 많이 소비하지 않고 시스템이 갑자기 수행 할 수있는 메커니즘이라는 것입니다 . 따라서 앱을보다 우아하게 종료 할 수 있도록 지속적인 저장 공간을 확보하고 지원하는 것이 좋습니다.
ToolmakerSteve

415

http://developer.android.com/reference/android/app/Activity.html의 활동 상태 문서에 따르면 영구 데이터 를 사용 하거나 사용 하는 것이 안전 하지 않습니다 .onSaveInstanceStateonRestoreInstanceState

문서에는 '활동 수명주기'섹션에 나와 있습니다.

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

다시 말해, 영구 데이터에 대한 저장 / 복원 코드를 onPause()and onResume()!

편집 : 자세한 설명을 위해 다음 onSaveInstanceState()문서가 있습니다.

이 메소드는 활동이 종료되기 전에 호출되므로 나중에 일정 시간이 지나면 상태를 복원 할 수 있습니다. 예를 들어, 활동 B가 활동 A 앞에서 시작되고 어떤 시점에서 활동 A가 자원을 재생하기 위해 종료 된 경우 활동 A는이 메소드를 통해 사용자 인터페이스의 현재 상태를 저장하여 사용자가 리턴 할 때 활성 (A)에, 사용자 인터페이스의 상태로 복원 할 수있는 onCreate(Bundle)onRestoreInstanceState(Bundle).


55
nitpick에 : 안전하지도 않습니다. 이것은 당신이 보존하고자하는 것과 얼마나 오래 지속되는지에 달려 있습니다. InstanceState는 현재 UI 상태 (컨트롤에 입력 된 데이터, 목록의 현재 위치 등)를 유지하는 데 완벽하지만 Pause / Resume은 장기 영구 스토리지의 유일한 가능성입니다.
Pontus Gagge

30
이것은 downvoted해야합니다. lifecycle 메소드와 같은 on (Save | Restore) InstanceState를 사용하는 것은 안전하지 않습니다 (즉, 상태를 저장 / 복원하는 것 이외의 다른 작업을 수행). 상태를 저장 / 복원하는 데 완벽하게 좋습니다. 또한 onPause 및 onResume에서 상태를 어떻게 저장 / 복원 하시겠습니까? 사용할 수있는 방법으로 번들을 얻지 못하므로 데이터베이스, 파일 등에서 다른 상태 절약을 사용해야합니다.
Felix

141
우리는 최소한이 문서를 검토하기 위해 노력한이 사람에게 투표하지 말아야합니다. 저는 사람들이 실제로 지식이 풍부한 커뮤니티를 구축하고 서로 투표하지 않기 위해 여기에 있다고 생각합니다. 그래서 노력에 1 투표하고 나는 사람들에게 투표하지 말고 투표하지 말라고 요청합니다 ....이 사람은 문서를 검토 할 때 갖고 싶은 혼란을 분명히합니다. 1 투표 :)
AZ_

21
나는이 대답이 공감할 가치가 있다고 생각하지 않습니다. Atleast는 대답하기 위해 노력했으며 doco의 섹션을 인용했습니다.
GSree

34
이 답변은 절대적으로 정확하며 다운 투표가 아닌 UP 투표가 필요합니다! 그것을 보지 못한 사람들에 대한 주들 사이의 차이점을 명확히하겠습니다. 선택한 라디오 버튼 및 입력 필드의 일부 텍스트와 같은 GUI 상태는 ListView에 표시되는 목록에 추가 된 레코드와 같이 데이터 상태보다 훨씬 덜 중요합니다. 후자는 유일한 보증 호출이므로 onPause의 데이터베이스에 저장해야합니다. 대신 onSaveInstanceState에 넣으면 호출되지 않은 데이터가 손실 될 위험이 있습니다. 그러나 라디오 버튼 선택이 같은 이유로 저장되지 않으면 큰 문제가 아닙니다.
JBM

206

내 동료는 상태 정보를 저장하는 방법 활동의 라이프 사이클 및 상태 정보에 대한 설명을 포함하여 안드로이드 장치에 응용 프로그램의 상태를 설명하는 기사를 쓴, 그리고 상태로 저장 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의 데이터에 대해 "이 데이터는 장치의 데이터베이스에 기록됩니다."라고 말합니다. 데이터가 파일 시스템의 앱 디렉토리에 파일에 저장되어 있다고 생각합니다.
Tom

2
@Tom SharefPrefs 데이터는 xml 파일에 기록됩니다. xml은 일종의 데이터베이스입니까? ;)
MaciejGórski

148

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

  • 최소한 레거시 버전 (개발 시점 / 시간 / 고정 방식이 확실하지 않음)에서 개발 중에 애플리케이션 스택 관리를 크게 복잡하게하는 미묘한 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에서) 앱을 시작할 때 발생하는 것으로 보이므로 개발자가이 문제를 많이 겪게됩니다. 또한 일부 앱 스토어 업데이트 메커니즘을 통해 사용자에게도 영향을 미칩니다.

내 주요 문제가 의도 된 프레임 워크 동작이 아니라이 버그라는 것을 깨닫기 전에 몇 시간 동안이 스레드를 해결했습니다. 훌륭한 글쓰기와해결 방법 (업데이트 : 아래 참조)는이 답변에서 사용자 @kaciula의 것으로 보입니다.

홈 키 누름 동작

2013 년 6 월 업데이트 : 몇 달 후 마침내 '올바른'솔루션을 찾았습니다. Stateful startedApp 플래그를 직접 관리 할 필요는 없으며 프레임 워크에서이를 감지하여 적절하게 구제 할 수 있습니다. 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같은 영구 저장소에 저장해야합니다.PreferencesSqlite


36
죄송합니다. 정확하지 않습니다. 활동을 다시 작성하기 전에 onSaveInstanceState가 호출됩니다. 즉, 사용자가 장치를 회전 할 때마다. 일시적인보기 상태를 저장하기위한 것입니다. 안드로이드가 응용 프로그램을 강제 종료하면 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를 표시하는 데 중요한 경로라는 점을 고려하십시오. 실제로 시간을 정하지 않았으므로 수정이 행복하지만 데이터베이스 파일을로드하고 여는 것이 빠르지 않습니까?
Tom

5
초보자가 앱에 잘라 붙여 넣어 바로 사용할 수있는 솔루션을 제공해 주셔서 대단히 감사합니다! @Tom 속도가 빠르면 1000 쌍을 저장하는 데 약 7 초가 걸리지 만 AsyncTask에서 수행 할 수 있습니다. 그러나 최종적으로 {cursor.close ()}를 추가해야합니다. 그렇지 않으면이 작업을 수행하는 동안 메모리 누수로 인해 충돌이 발생합니다.
Noumenon

3
나는 이것을 발견했으며 깔끔하게 보이지만 최근에 작업하고있는 장치 인 Google Glass에서 이것을 사용하는 것을 주저합니다.
Stephen Tetreault

61

나는 대답을 찾았다 고 생각한다. 내가 한 일을 간단한 단어로 말해 드리겠습니다.

activity1과 activity2라는 두 가지 활동이 있고 activity1에서 activity2로 탐색하고 (activity2에서 일부 작업을 수행 한 후) activity1의 단추를 클릭하여 다시 활동 1로 돌아 간다고 가정하십시오. 이제이 단계에서 나는 activity2로 돌아가고 싶었고 마지막으로 activity2를 떠났을 때와 같은 상태로 activity2를보고 싶습니다.

위의 시나리오에서 내가 한 일은 매니페스트에서 다음과 같이 변경했다는 것입니다.

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

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

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

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

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

이제 어떻게 될지는 activity2에서 변경 한 내용이 손실되지 않고 이전에 떠난 것과 같은 상태로 activity2를 볼 수 있다는 것입니다.

나는 이것이 답이라고 생각하며 이것은 나에게 잘 작동한다. 내가 틀렸다면 나를 바로 잡으십시오.


2
@ bagusflyer 더 구체적으로 관리 ??? 귀하의 의견은 도움이되지 않으며 아무도이를 근거로 도움을 줄 수 없습니다.
Stephen Tetreault

2
이것은 다른 상황에 대한 해답입니다. 동일한 앱 내의 두 가지 활동. OP는 앱을 종료 하는 것입니다 (예 : 홈 버튼 또는 다른 앱으로 전환하는 다른 방법).
ToolmakerSteve

44

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

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

onPause ()onResume () 도 무료 메소드입니다. onPause ()는 Activity가 종료 될 때 항상 호출됩니다 (예 : finish () 호출로). 이것을 사용하여 현재 메모를 데이터베이스에 다시 저장합니다. 모범 사례는 onPause () 중에 해제 할 수있는 모든 리소스를 해제하여 수동 상태에있을 때 더 적은 리소스를 사용하는 것입니다.


40

실제로 onSaveInstanceState()활동이 백그라운드로 갈 때 호출됩니다.

문서에서 인용 : "이 메소드는 활동이 종료되기 전에 호출되므로 나중에 다시 돌아올 때 상태를 복원 할 수 있습니다." 출처


37

도움말을 나는 다음을 사용하는 상용구를 감소 interfaceclass읽기 / 쓰기 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);
    }

}

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


34

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

Bundle savedInstanceState & Co

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

그리고 구글은 그 자체로는 신뢰할 수 없다고 말합니다.

내 방법은 변경 사항을 환경 설정에 즉시 저장하는 것입니다.

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

어떤 방식 으로든 SharedPreferences는 번들과 유사하게 작동합니다. 그리고 자연스럽고 처음에는 이러한 값을 선호도에서 읽어야합니다.

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

이 개념을 적용 할 때 액티비티가 처음으로 열린 상태인지 아니면 다시 스택으로 인해 다시 열린 지 여부에 관계없이 활동은 마지막으로 저장된 상태를 계속 사용합니다.


31

원래 질문에 직접 대답합니다. 활동이 다시 작성되지 않으므로 savedInstancestate가 널입니다.

다음과 같은 경우에만 활동이 상태 번들로 다시 작성됩니다.

  • 방향 또는 전화 언어 변경과 같은 구성 변경으로 인해 새 활동 인스턴스를 작성해야 할 수도 있습니다.
  • OS가 활동을 파괴 한 후 백그라운드에서 앱으로 돌아갑니다.

Android는 메모리가 부족하거나 오랜 시간 동안 백그라운드에 있었던 후 백그라운드 활동을 삭제합니다.

Hello World 예제를 테스트 할 때 활동을 종료하고 돌아 오는 몇 가지 방법이 있습니다.

  • 뒤로 버튼을 누르면 활동이 완료됩니다. 앱을 다시 시작하는 것은 새로운 사례입니다. 당신은 전혀 백그라운드에서 재개되지 않습니다.
  • 홈 버튼을 누르거나 작업 전환기를 사용하면 활동이 배경으로 이동합니다. 응용 프로그램으로 돌아갈 때 onCreate는 활동을 파괴 해야하는 경우에만 호출됩니다.

대부분의 경우 집을 눌렀다가 앱을 다시 시작하면 활동을 다시 만들 필요가 없습니다. 메모리에 이미 존재하므로 onCreate ()가 호출되지 않습니다.

설정-> 개발자 옵션 아래에 "활동을 유지하지 마십시오"라는 옵션이 있습니다. 활성화되면 Android는 항상 활동을 파괴하고 배경이있을 때 다시 생성합니다. 최악의 시나리오를 시뮬레이트하기 때문에 개발시 활성화 상태로 두는 것이 좋습니다. (당신의 활동을 항상 재활용하는 저용량 메모리 장치).

다른 답변은 상태를 저장하는 올바른 방법을 가르쳐 준다는 점에서 가치가 있지만 코드가 예상대로 작동하지 않는 이유는 실제로 답변하지 않았다고 생각합니다.


28

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


2
귀하의 경우 onCreateonRestoreInstanceState(가) 때문이라고되지 않는 Activity당신이 응용 프로그램을 전환 할 때 전혀 파괴되지 않으므로 아무것도 복원 할 필요가 없다. 안드로이드는 onSaveInstanceState나중에 활동이 파괴되는 경우를 대비하여 호출 합니다 (전체 장치 구성이 변경되어 활동을 처음부터 다시 작성해야하기 때문에 화면을 회전 할 때 100 % 확실하게 발생합니다).
Vicky Chijwani 2016 년

20

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

mySavedInstanceState=savedInstanceState;

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

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

위의 제안 onSaveInstanceStateonRestoreInstanceState같이 사용 하지만 변수가 변경되면 변수를 저장하는 방법을 사용할 수도 있습니다 (예 : 사용 putBoolean)


19

허용되는 답변은 맞지만 Icepick 이라는 라이브러리를 사용하여 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.


16

활동이 작성되면 onCreate () 메소드가 호출됩니다.

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

savedInstanceState는 처음으로 null 인 Bundle 클래스의 객체이지만 다시 생성 될 때 값을 포함합니다. 활동 상태를 저장하려면 onSaveInstanceState ()를 재정의해야합니다.

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

outState.putString ( "key", "Welcome Back")과 같은 "outState"Bundle 객체에 값을 넣고 super를 호출하여 저장합니다. 활동이 파괴되면 상태는 Bundle 객체에 저장되며 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");
    }

15

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

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

}

11

여기에 Steve Moseley 의 답변 ( ToolmakerSteve )이 의견을 제시합니다 ( 전체 onSaveInstanceState 대 onPause, 동쪽 비용 대 서쪽 비용 사가)

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


10

코 틀린 코드 :

저장:

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()먼저 SaveInstanceState(Bundle savedInstanceState)메소드 를 재정 의하여 savedInstanceState에 데이터를 저장해야합니다 .

활동 destroy SaveInstanceState(Bundle savedInstanceState)메소드가 호출되고 거기에 저장하려는 데이터를 저장합니다. 그리고 onCreate()활동이 다시 시작될 때도 마찬가지입니다 . (활동이 파괴되기 전에 일부 데이터를 저장했기 때문에 saveedInstanceState는 null이 아닙니다)


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

액티비티, 프래그먼트 또는 번들에서 상태를 직렬화해야하는 모든 오브젝트 (예 : 박격포의 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
@ralphspoon 예, Fragment 및 Custom View에서 작동합니다. 예제 코드를 확인하십시오. 내 답변을 편집했습니다. 더 많은 코드 샘플을 찾으 려면 github.com/frankiesardo/icepick 공식 문서를 방문하는 것이 좋습니다 .
탄 Phearum

@ ChetanMehra 당신은 커스텀 뷰 클래스를 의미합니까? 사용자 정의보기 인 경우 위의 CustomView 예제와 같이 onSaveInstanceState 및 onRestoreInstanceState를 대체 할 수 있습니다.
탄 Phearum

예를 들어 I 평균 클래스 객체 내부 뷰 클래스 : {A @state를 ClassA;} 클래스있는 CustomView보기를 확장 또는 클래스있는 CustomView는 {@ 국가 내부 클래스 {}}보기 확장
Chetan에 메라

@THANNPhearum 다른 질문으로 물어봐야합니까?
Chetan Mehra

내가 참조. 그렇다면 ClassA는 소포 가능해야합니다. 언급했듯이 활동, 조각 또는 번들에서 상태를 직렬화 해야하는 모든 객체에 적용됩니다.
THANN Phearum

6

솔루션이 찌그러 지는지 확실하지 않지만 바운드 서비스를 사용하여 ViewModel 상태를 유지합니다. 서비스의 메모리에 저장하거나 SQLite 데이터베이스에서 유지 및 검색하는지 여부는 요구 사항에 따라 다릅니다. 이것은 어떤 풍미의 서비스가하는지, 애플리케이션 상태 및 추상적 인 공통 비즈니스 로직 유지와 같은 서비스를 제공합니다.

모바일 장치에 고유 한 메모리 및 처리 제한으로 인해 웹 페이지와 유사한 방식으로 Android보기를 처리합니다. 이 페이지는 상태를 유지하지 않고 순전히 응용 프로그램 상태를 표시하고 사용자 입력을 받아들이는 것이 목적인 프리젠 테이션 레이어 구성 요소입니다. 웹 앱 아키텍처의 최신 트렌드는 오래된 MVC (Model, View, Controller) 패턴을 사용합니다. 여기서 페이지는보기이고 도메인 데이터는 모델이며 컨트롤러는 웹 서비스 뒤에 있습니다. 뷰는 모델과 도메인 데이터이며 컨트롤러는 안드로이드 바운드 서비스로 구현됩니다. 보기가 컨트롤러와 상호 작용하기를 원할 때마다 시작 / 다시 시작시 바인딩하고 중지 / 일시 중지시 바인드 해제하십시오.

이 접근 방식을 사용하면 모든 응용 프로그램 비즈니스 논리를 서비스로 이동하여 여러 뷰에서 중복 된 논리를 줄이고 뷰에서 다른 중요한 설계 원칙 인 단일 책임을 적용 할 수 있다는 점에서 분리 분리 설계 원칙을 강화할 수 있습니다.


5

코 틀린

지속적으로 유지하려는 변수를 재정의 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는 상태 저장을위한 ViewModel 을 제공 하므로 saveInstanceState 대신 해당 모델을 사용해보십시오.


3
사실이 아닙니다. 문서에서 : "저장된 인스턴스 상태와 달리, ViewModels는 시스템이 시작한 프로세스 종료 중에 소멸됩니다. 따라서 ViewModel 오브젝트를 onSaveInstanceState () (또는 일부 다른 디스크 지속성)와 함께 사용하여 savedInstanceState의 식별자를 숨겨서 볼 수 있습니다. 모델은 시스템 사망 후 데이터를 다시로드합니다. "
Vyacheslav Martynenko

백그라운드에서 권한이 변경 되어이 문제가 발생했습니다.
Brill Pappin

문서에서 "시스템 시작 프로세스 종료를 처리해야하는 경우 onSaveInstanceState ()를 백업으로 사용할 수 있습니다."에 동의합니다.
Zhar

2

메소드를 구현하지 않고 Android에서 상태를 저장하는 방법이 있습니다. 다음 선언을 활동 선언의 매니페스트에 추가하십시오.

android:configChanges="orientation|screenSize"

다음과 같아야합니다.

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

여기이 속성에 대한 자세한 정보가 있습니다.

수동으로 처리하는 것보다 Android에서이를 처리하도록하는 것이 좋습니다.


2
이것은, 당신은 단지 방향 변경을주는 상태를 저장 함께 할 수 없다, 당신은 응용 프로그램을 다시 시작하고 일시 정지 및 다른 이벤트에 대한 언제든지 재개 될 수 염두에 두어야
주 - 랄프 - 아돌프

1
이 답변은 오리엔테이션이 변경되었을 때 상태를 저장하고 복잡한 방법의 이해와 구현을 피하고자하는 사람들을위한 것입니다.
IgniteCoders

공정하게 나는 당신의 요점을 알았습니다. 상태를 저장하기 위해 고군분투하는 대부분의 사람들은 조각을 사용하고 있다고 생각합니다. 활동이 실제로 ID를 가진 한 UI 구성 요소의 통계를 저장하기 때문입니다. 그러나 조각은 더 특별합니다. 다시 한 번 저장 인스턴스 통계는 다루기가
어려웠

그것은 작동합니다 ... 감사합니다
Fanadez

1

무엇을 저장하고 무엇을하지 말아야합니까?

EditText방향이 바뀌는 동안 왜 텍스트 가 자동으로 저장 되는지 궁금한 적이 있습니까? 이 답변은 당신을위한 것입니다.

활동 인스턴스가 소멸되고 시스템이 새 인스턴스 (예 : 구성 변경)를 다시 작성하는 경우 이전 활동 상태 ( 인스턴스 상태) 의 저장된 데이터 세트를 사용하여이를 재 작성하려고합니다. ) .

인스턴스 상태는 Bundle 객체에 저장된 키-값 쌍 .

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

  • 텍스트 EditText
  • 에서 스크롤 위치 ListView

인스턴스 상태의 일부로 다른 변수를 저장해야하는 경우 무시 해야합니다. onSavedInstanceState(Bundle savedinstaneState) 메서드 .

예를 들어 int currentScore GameActivity에서

데이터를 저장하는 동안 onSavedInstanceState (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의 텍스트가 저장되지 않습니다.

활동 상태를 복원하기 위해 선택할 수있는 것은 무엇입니까?

 onCreate(Bundle savedInstanceState)

또는

onRestoreInstanceState(Bundle savedInstanceState)

두 방법 모두 동일한 Bundle 객체를 가져 오므로 실제로 복원 논리를 작성하는 위치는 중요하지 않습니다. 유일한 차이점은 onCreate(Bundle savedInstanceState)메서드에서 후자의 경우 필요하지 않지만 null 검사를 제공해야한다는 것입니다. 다른 답변에는 이미 코드 스 니펫이 있습니다. 당신은 그들을 참조 할 수 있습니다.

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 계층 구조를 복원하도록 항상 호출

보너스

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

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

관련 링크 :

기본 동작에 대한 데모
Android Official Documentation .


1

이제 뷰 모델에서 두 가지 방법을 수행하는 것이 좋습니다. 첫 번째를 저장된 인스턴스로 저장하려는 경우 : https://developer.android.com/topic/libraries/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.