활동간에 데이터를 공유하는 가장 좋은 방법은 무엇입니까?


239

앱 전체에서 사용되는 주요 활동 인 하나의 활동이 있으며 여러 변수가 있습니다. 첫 번째 활동의 데이터를 사용할 수있는 두 가지 다른 활동이 있습니다. 이제 다음과 같이 할 수 있다는 것을 알고 있습니다.

GlobalState gs = (GlobalState) getApplication();
String s = gs.getTestMe();

그러나 나는 많은 변수를 공유하고 싶고 일부는 다소 클 수 있으므로 위와 같이 사본을 만들고 싶지 않습니다.

get 및 set 메소드를 사용하지 않고 변수를 직접 가져오고 변경하는 방법이 있습니까? Google 개발자 사이트에서 Android에서의 성능에 권장되지 않는 기사를 읽은 것을 기억합니다.


2
Android 2.3 (Gingerbread)부터 Dalvik은 get / set 최적화를 자동으로 수행합니다. 이는 이전 버전의 Android를 대상으로하는 경우에만 해당됩니다.
StellarVortex

이 예제는 문자열 데이터를 복사하지 않습니다. 오히려 동일한 문자열 객체에 대한 참조를 만듭니다.
코드 견습생

믿기 ​​어렵습니다. 왜 다른 활동에서 활동을 시작하고 첫 번째에서 두 번째로 복잡한 개체를 전달할 가능성이 없습니까? 직렬화없이 개체와 모든 노력을 저장하십시오. 두 가지 활동이 모두 동일한 앱에있는 경우 이것이 보안상의 허점입니까 아니면 다른 이유가 단순히 객체 참조를 전달하지 않는 것입니까? (앱이 다르면 다릅니다.)
Droidum


LiveData는 가장 최신 솔루션입니다. 아래에서 내 대답을 확인하십시오.
Amir Uval

답변:


476

여기에 이것을 달성하는 가장 일반적인 방법 의 편집 :

  • 의도 내에서 데이터 보내기
  • 정적 필드
  • 해시지도 WeakReferences
  • 객체 유지 (sqlite, 공유 환경 설정, 파일 등)

TL; DR : 데이터를 공유하는 두 가지 방법이 있습니다 : 데이터를 인 텐트로 전달하거나 다른 곳에 저장하는 것. 데이터가 기본 요소, 문자열 또는 사용자 정의 오브젝트 인 경우 : 의도 추가 항목의 일부로 전송하십시오 (사용자 정의 오브젝트는을 구현해야합니다 Parcelable). 복잡한 객체를 전달하는 경우 인스턴스를 다른 곳에 싱글 톤으로 저장하고 시작된 액티비티에서 액세스합니다.

각 접근 방식을 구현하는 방법과 이유에 대한 몇 가지 예 :

의도 내에서 데이터 보내기

Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
intent.putExtra("some_key", value);
intent.putExtra("some_other_key", "a value");
startActivity(intent);

두 번째 활동에서 :

Bundle bundle = getIntent().getExtras();
int value = bundle.getInt("some_key");
String value2 = bundle.getString("some_other_key");

기본 데이터 또는 문자열을 전달하는 경우이 방법을 사용하십시오 . 또한 구현하는 객체를 전달할 수도 있습니다 Serializable.

유혹적이지만 사용하기 전에 두 번 생각해야합니다 Serializable. 오류가 발생하기 쉽고 엄청나게 느립니다. 따라서 일반적으로 가능하면 멀리Serializable 하십시오. 복잡한 사용자 정의 객체를 전달 하려면 Parcelable인터페이스를 살펴보십시오 . 구현하기는 어렵지만에 비해 상당한 속도 향상이 Serializable있습니다.

디스크를 유지하지 않고 데이터 공유

대부분의 경우 두 활동이 동일한 프로세스에서 실행되는 경우 메모리에 저장하여 활동간에 데이터를 공유 할 수 있습니다.

참고 : 때때로 사용자가 활동을 종료하지 않고 종료 할 때 (Android는) 응용 프로그램을 종료하기로 결정할 수 있습니다. 이러한 시나리오에서 안드로이드가 앱이 종료되기 전에 제공된 의도를 사용하여 마지막 활동을 시작하려는 경우를 경험했습니다. 이 경우 싱글 톤 (귀하의 또는 Application)에 저장된 데이터 는 사라지고 나쁜 일이 발생할 수 있습니다. 이러한 경우를 피하려면 오브젝트를 디스크에 지속 시키거나 사용하기 전에 데이터를 점검하여 유효한지 확인하십시오.

싱글 톤 클래스 사용

데이터를 보관할 수업을 준비하십시오.

public class DataHolder {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}

  private static final DataHolder holder = new DataHolder();
  public static DataHolder getInstance() {return holder;}
}

시작된 활동에서 :

String data = DataHolder.getInstance().getData();

응용 프로그램 싱글 톤 사용

응용 프로그램 싱글 톤은 android.app.Application앱이 시작될 때 생성 되는 인스턴스입니다 . 다음을 확장하여 사용자 정의 항목을 제공 할 수 있습니다 Application.

import android.app.Application;
public class MyApplication extends Application {
  private String data;
  public String getData() {return data;}
  public void setData(String data) {this.data = data;}
}

활동을 시작하기 전에 :

MyApplication app = (MyApplication) getApplicationContext();
app.setData(someData);

그런 다음 시작된 활동에서 :

MyApplication app = (MyApplication) getApplicationContext();
String data = app.getData();

정적 필드

아이디어는 기본적으로 싱글 톤과 동일하지만이 경우 데이터에 정적 액세스를 제공합니다.

public class DataHolder {
  private static String data;
  public static String getData() {return data;}
  public static void setData(String data) {DataHolder.data = data;}
}

시작된 활동에서 :

String data = DataHolder.getData();

해시지도 WeakReferences

같은 생각이지만 가비지 수집기가 참조되지 않은 객체를 제거하도록 허용 (예 : 사용자가 활동을 종료 한 경우)

public class DataHolder {
  Map<String, WeakReference<Object>> data = new HashMap<String, WeakReference<Object>>();

  void save(String id, Object object) {
    data.put(id, new WeakReference<Object>(object));
  }

  Object retrieve(String id) {
    WeakReference<Object> objectWeakReference = data.get(id);
    return objectWeakReference.get();
  }
}

활동을 시작하기 전에 :

DataHolder.getInstance().save(someId, someObject);

시작된 활동에서 :

DataHolder.getInstance().retrieve(someId);

인 텐트의 엑스트라를 사용하여 객체 ID를 전달해야 할 수도 있고 아닐 수도 있습니다. 그것은 모두 특정 문제에 달려 있습니다.

디스크에 객체 유지

아이디어는 다른 활동을 시작하기 전에 디스크에 데이터를 저장하는 것입니다.

장점 : 다른 곳에서 활동을 시작할 수 있으며 데이터가 이미 지속되면 제대로 작동합니다.

단점 : 번거롭고 구현하는 데 시간이 더 걸립니다. 더 많은 코드가 필요하므로 버그가 발생할 가능성이 높습니다. 또한 훨씬 느려질 것입니다.

객체를 유지하는 몇 가지 방법은 다음과 같습니다.


11
나는 이것이 더 크고 복잡한 데이터를위한 "정상적인"방법이 아니라고 주장 할 것이다. 정적 싱글 톤이나 응용 프로그램 객체를 사용하는 것이 훨씬 쉬우 며 훌륭하게 작동합니다. 이제 OP는 예제에서 문자열을 사용 했으므로 인 텐트가 완벽하고 선호됩니다.
Charlie Collins

10
Serializable은 Android 프로세스 모델에서 심각한 성능 문제가있는 것으로 나타났습니다. 그래서 그들은 Parcelable을 소개했습니다. 위의 답변에서 Serializable 대신 Parcelable 을 읽으십시오 .
Subin Sebastian

3
그것은 setResult방법을 통해 이루어집니다 . 또한이 경우 보조 활동은 startActivityForResult메소드를 사용하여 호출해야합니다 .
Cristian

2
훌륭한 요약! 싱글의 문제가 파괴되고 관련하여,이 활동을 많이하고 개체 애플 리케이션을위한 간단한 솔루션입니다 : 사용의 서브 클래스 Activity에 걸쳐, 그것의 onCreate()응용 프로그램을 시작할 때 기입하는 싱글 톤의 체크 정적 필드. 해당 필드가 비어 있으면 FLAG_ACTIVITY_CLEAR_TASK또는 a BroadcastReceiver를 사용하여 시작 활동으로 돌아가서 다른 활동을 종료하십시오.
Janosch

1
응용 프로그램 클래스에 데이터를 저장하지 않는 것이 좋습니다. 자세한 내용은 여기 읽기 : developerphil.com/dont-store-data-in-the-application-object
Shayan_Aryan

22

사용할 수있는 것 :

  1. 활동간에 데이터 전달 (Cristian이 말한 것처럼)
  2. 정적 변수가 많은 클래스 사용 (클래스 인스턴스없이 getter / setter를 사용하지 않고 호출 할 수 있음)
  3. 데이터베이스 사용
  4. 공유 환경 설정

당신이 선택하는 것은 당신의 필요에 달려 있습니다. 아마도 당신은 "많이"있을 때 하나 이상의 방법을 사용할 것입니다


1
정적 프로세스의 죽음에 지워지고 있습니다
EpicPandaForce

물론 @EpicPandaForce와 장치가 꺼 졌을 때.
WarrenFaith

1
그러나 기기가 꺼져 있으면 앱이 MAIN작업 에서 다시 시작됩니다 . 프로세스 종료 후 마지막으로 열린 활동을 다시 시작하면 앱의 깊숙한 곳에서 세부 정보 페이지가 될 수 있습니다.
EpicPandaForce 2016 년

@EpicPandaForce는 Cpt Obvious 인 것처럼 내 아이러니를 놓친 것 같습니다.
WarrenFaith

16

당신이 구글 명령을 수행하십시오! 여기 : http://developer.android.com/resources/faq/framework.html#3

  • 기본 데이터 유형
  • 비 영구 객체
  • 싱글턴 수업-내가 좋아하는 것 : D
  • 공개 정적 필드 / 방법
  • 객체에 대한 약한 참조의 HashMap
  • 영구 객체 (응용 프로그램 환경 설정, 파일, contentProviders, SQLite DB)


안티 패턴 만, Singleton 클래스는 첫 번째 디자인 패턴이며 정적 필드 / 메소드가있는 클래스는 싱글 톤과 매우 유사하게 작동하며 busnis 객체를 유지하기위한 데이터베이스를 만드는 것은 좋지 않은 일입니다. 그것을 달성하는 방법, 그러나 나는 왜 구글이 그렇게 매우 어리석은 일, 성능 문제 또는 무엇을 복잡한 지 궁금합니다! 또는 안드로이드 방식을 이해하지 못하고 있습니까 ?? !!!!
La VloZ Merrill

링크가 끊어졌습니다 :(
Shayan_Aryan

14

"그러나 많은 변수를 공유하고 싶고 일부는 다소 클 수 있으므로 위와 같이 사본을 만들고 싶지 않습니다."

그것은 복사를하지 않습니다 (특히 String 을 사용하지만 객체조차도 객체 자체가 아닌 참조 값으로 전달되며 getter는 사용하기에 적합합니다. 공통적이고 다른 방법보다 사용하는 것이 더 좋습니다) 잘 이해했습니다). getter 및 setter를 사용하지 않는 것과 같은 오래된 "성능 신화"는 여전히 가치가 있지만 docs에서도 업데이트되었습니다 .

그러나 그렇게하고 싶지 않으면 GlobalState 에서 변수를 공개하거나 보호 하여 직접 액세스 할 수도 있습니다. 또한 Application 객체 JavaDoc이 나타내는 대로 정적 싱글 톤을 만들 수 있습니다 .

일반적으로 Application을 서브 클래 싱 할 필요가 없습니다. 대부분의 상황에서 정적 싱글 톤은보다 기능적인 방식으로 동일한 기능을 제공 할 수 있습니다. 싱글 톤에 글로벌 컨텍스트가 필요한 경우 (예 : 브로드 캐스트 수신기 등록) 싱글 톤을 처음 생성 할 때 내부적으로 Context.getApplicationContext ()를 사용하는 컨텍스트를 검색하는 함수를 검색 할 수 있습니다.

다른 답변에서 언급 했듯이 인 텐트 데이터를 사용하면 데이터를 전달하는 또 다른 방법이지만 일반적으로 작은 데이터 및 간단한 유형에 사용됩니다. 더 크고 복잡한 데이터를 전달할 수 있지만 정적 싱글 온을 사용하는 것보다 더 복잡합니다. 응용 프로그램 객체는 여전히 (이 안드로이드 응용 프로그램에서 잘 정의 된 라이프 사이클을 가지고 있기 때문에)하지만 안드로이드 응용 프로그램 구성 요소 사이에 큰 / 더 복잡한 비 영구 데이터를 공유하기위한 내 개인 좋아하는 것입니다.

또한 다른 사람들이 지적했듯이 데이터가 매우 복잡 하고 지속적이어야 하는 경우 SQLite 또는 파일 시스템을 사용할 수 있습니다.


1
실제로, 나는 최근에 문서에서 developer.android.com/guide/appendix/faq/framework.html#3 에서 이것을 우연히 발견 했습니다 . "비 지속적인 복잡한 객체"의 경우 Application 클래스를 사용하여 정적 싱글 톤을 생성 및 해제하는 것이 좋습니다! 그렇게하면 응용 프로그램이 제공하는 잘 정의 된 수명주기와 정적 싱글 톤을 쉽게 사용할 수 있습니다.
Charlie Collins

2
자주 묻는 질문의 해당 섹션이 제거 된 것 같습니다 ( "비 지속적 개체"만 있고 Application 클래스에 대한 언급은 없음) 어쨌든 당신은 정교한 수 있습니까?
Tony Chan

1
현재 developer.android.com/guide/faq/framework.html#3 "하나의 응용 프로그램 내에서 활동 / 서비스간에 데이터를 전달하는 방법은 무엇입니까?"이며 Application 클래스에 대한 언급은 없습니다.
Jerry101

"Android in Action"에서 설정 한 선례에 따라 Application 객체를 사용하고 싶습니다. 그러나 많은 장래의 고용주는 코드 문제에서 이것을 볼 때 그것을 좋아하지 않습니다. 그들은 틀렸을 수 있지만, 그들의 무게를 던졌습니다. BTW : 'framework.html # 3 링크가 더 이상 작동하지 않습니다.
Matt J.


4

활동간에 데이터를 공유하는 새롭고 더 나은 방법이 있으며 LiveData 입니다. 특히 Android 개발자 페이지에서이 인용문을 확인하십시오.

LiveData 객체가 수명주기를 인식한다는 사실은 여러 활동, 프래그먼트 및 서비스간에 객체를 공유 할 수 있음을 의미합니다. 예제를 단순하게 유지하기 위해 LiveData 클래스를 싱글 톤으로 구현할 수 있습니다.

이것의 의미는 엄청납니다. 모든 모델 데이터는 LiveData래퍼 내부의 일반적인 싱글 톤 클래스에서 공유 될 수 있습니다 . ViewModel테스트 가능성을 위해 액티비티에서 각각으로 주입 될 수 있습니다 . 더 이상 메모리 누수를 방지하기 위해 약한 참조에 대해 걱정할 필요가 없습니다.


2

위에서 설명한 http://developer.android.com/guide/faq/framework.html 에서 약한 참조 방식의 해시 맵을 사용하면 문제가되는 것 같습니다. 맵 값뿐만 아니라 전체 항목을 어떻게 회수합니까? 어떤 범위에 할당합니까? 프레임 워크가 활동 라이프 사이클을 제어하므로 참여 활동 중 하나를 소유하면 소유자가 클라이언트보다 먼저 소멸 될 때 런타임 오류가 발생할 위험이 있습니다. 애플리케이션이이를 소유 한 경우, 일부 활동은 해시 맵이 유효한 키와 잠재적으로 가비지 수집 된 약한 참조를 가진 항목을 보유하지 않도록 항목을 명시 적으로 제거해야합니다. 또한 키에 대해 반환 된 값이 null 인 경우 클라이언트는 어떻게해야합니까?

응용 프로그램이 소유하거나 단일 톤 내에있는 WeakHashMap이 더 나은 선택 인 것 같습니다. 지도의 값은 키 객체를 통해 액세스되며 키에 대한 강력한 참조가 존재하지 않는 경우 (즉, 모든 활동이 키와 매핑 된 것으로 수행됨) GC는지도 항목을 회수 할 수 있습니다.


2

활동간에 데이터를 공유하는 다양한 방법이 있습니다

1 : 의도를 사용하여 활동간에 데이터 전달

Intent intent=new Intent(this, desirableActivity.class);
intent.putExtra("KEY", "Value");
startActivity(intent)

2 : static 키워드를 사용하여 변수를 public static으로 정의하고 프로젝트의 모든 위치를 사용하십시오.

      public static int sInitialValue=0;

classname.variableName을 사용하여 프로젝트의 어느 곳에서나 사용하십시오.

3 : 데이터베이스 사용

그러나 비트 프로세스가 길면 쿼리를 사용하여 데이터를 삽입하고 필요할 때 커서를 사용하여 데이터를 반복해야합니다. 그러나 캐시를 정리하지 않고 데이터를 잃을 가능성은 없습니다.

4 : 공유 환경 설정 사용

데이터베이스보다 훨씬 쉽습니다. 그러나 ArrayList, List 및 custome 객체를 저장할 수없는 몇 가지 제한 사항이 있습니다.

5 : Aplication 클래스에서 getter setter를 작성하고 프로젝트의 모든 위치에 액세스하십시오.

      private String data;
      public String getData() {
          return data;
      }

      public void setData(String data) {
          this.data = data;
      }

여기에 설정하고 활동에서 얻을

         ((YourApplicationClass)getApplicationContext()).setData("abc"); 

         String data=((YourApplicationClass)getApplicationContext()).getData();  

1

글쎄, 나는 몇 가지 아이디어가 있지만 그들이 당신이 찾고있는 것인지 모르겠다.

모든 데이터를 보유한 서비스를 사용하고 데이터 검색을 위해 활동을 서비스에 바인딩 할 수 있습니다.

또는 데이터를 직렬화 가능 또는 소포 가능으로 패키지하여 번들에 첨부하고 활동 사이에 번들을 전달하십시오.

이것은 당신이 찾고있는 것이 아닐 수도 있지만, SharedPreferences 또는 기본 설정을 사용해 볼 수도 있습니다.

어느 쪽이든 당신이 결정한 것을 알려주십시오.



1

당신의 의도가 현재의 활동에서 다른 활동을 호출하는 경우, 당신은 사용해야 텐트를 . 필요에 따라 데이터를 공유하는 것보다 데이터를 유지하는 데 집중할 수 없습니다.

그러나 이러한 값을 실제로 유지해야하는 경우 로컬 저장소의 일종의 구조화 된 텍스트 파일 또는 데이터베이스에 유지할 수 있습니다. 특성 파일, XML 파일 또는 JSON 파일은 데이터를 저장하고 활동 작성 중에 쉽게 구문 분석 할 수 있습니다. 모든 Android 장치에 SQLite가 있으므로 데이터베이스 테이블에 저장할 수 있습니다. 맵을 사용하여 키-값 쌍을 저장하고 맵을 로컬 스토리지에 직렬화 할 수도 있지만 간단한 데이터 구조에는 유용하지 않을 수 있습니다.


1

위에서 언급 한 모든 대답은 훌륭합니다 ... 활동을 통해 데이터를 유지하는 것에 대해 아직 언급하지 않은 사람을 추가하고 있습니다. 즉, 내장 된 Android SQLite 데이터베이스를 사용하여 관련 데이터를 유지하는 것입니다 ... 사실 당신은 응용 프로그램 상태의 databaseHelper를 활성화하고 활성화하는 동안 필요에 따라 호출하십시오. 또는 필요할 때 도우미 클래스를 만들고 DB를 호출하십시오 ... 다른 레이어를 추가하기 만하면됩니다. 그러나 다른 모든 답변으로 충분합니다. 뿐만 아니라 .. 정말 선호


1

로그인 후 이메일을 전달하는 활동 사이의 데이터 공유 예

"이메일"은 요청중인 활동의 값을 참조하는 데 사용할 수있는 이름입니다.

로그인 페이지의 1 코드

Intent openLoginActivity = new Intent(getBaseContext(), Home.class);
    openLoginActivity.putExtra("email", getEmail);

홈페이지의 2 개 코드

Bundle extras = getIntent().getExtras();
    accountEmail = extras.getString("email");

1

그리고 데이터 객체로 작업하려면이 두 가지 구현이 매우 중요합니다.

직렬화 가능 및 소포 가능

  • Serializable은 마커 인터페이스로, 사용자는 요구 사항에 따라 데이터를 마샬링 할 수 없습니다. 따라서 객체가 Serializable을 구현하면 Java가 자동으로 직렬화합니다.
  • Parcelable은 안드로이드 자체 직렬화 프로토콜입니다. Parcelable에서 개발자는 마샬링 및 마샬링 해제를위한 사용자 지정 코드를 작성합니다. 따라서 직렬화에 비해 가비지 객체가 적습니다.
  • Parcelable의 성능은 사용자 정의 구현으로 인해 Serializable과 비교할 때 매우 높습니다. Android에서 객체를 직렬화 할 때 Parcelable 주입을 사용하는 것이 좋습니다.

public class User implements Parcelable

여기 에서 더 확인 하십시오

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.