번들을 통해 개체를 보내는 방법


119

번들을 통해 대부분의 처리를 수행하는 클래스에 대한 참조를 전달해야합니다.

문제는 의도 또는 컨텍스트와 관련이 없으며 많은 양의 비 원시 객체가 있다는 것입니다. 클래스를 parcelable / serializable로 패키징하고 어떻게 전달 startActivityForResult합니까?


2
"내 처리의 대부분을 번들을 통해 수행하는 클래스에 대한 참조를 전달해야합니다."-그 이유는 무엇입니까?
CommonsWare 2010

1
나는 객체 (DataManager)를 가지고 있으며, 서버를 처리하고 일부 GUI에 대해 몇 가지 백엔드를 실행합니다. 새 연결이 설정 될 때마다 사용자가 ListView에 모든 활성 연결을 나열하고 사용자가 하나를 선택하도록하는 새 활동을 시작할 수 있기를 바랍니다. 그러면 결과 데이터가 새 GUI에 연결됩니다. 이것은 실제로 백엔드의 스킨 선택기입니다.
ahodder

3
여러 활동에서 동일한 객체 인스턴스를 처리하는 경우 싱글 톤 패턴 을 고려할 수 있습니다 . 여기에 좋은 튜토리얼이 있습니다 .
sotrh

답변:


55

어떤 길을 택해야하는지 알아 내기 위해서는 CommonsWare의 핵심 질문 인 "왜"뿐 아니라 "무엇을 위해?"라는 질문에 답해야합니다. 통과하고 있습니까?

현실은 번들을 통과 할 수있는 유일한 것은 일반 데이터라는 것입니다. 다른 모든 것은 해당 데이터가 의미하거나 가리키는 것에 대한 해석을 기반으로합니다. 문자 그대로 객체를 전달할 수는 없지만 다음 세 가지 중 하나를 수행 할 수 있습니다.

1) 개체를 구성 데이터로 분해 할 수 있으며 다른 쪽 끝에있는 개체가 동일한 종류의 개체에 대해 알고 있으면 직렬화 된 데이터에서 복제본을 조합 할 수 있습니다. 이것이 대부분의 일반적인 유형이 번들을 통과하는 방법입니다.

2) 불투명 핸들을 전달할 수 있습니다. 핸들이 될 동일한 컨텍스트 내에서 전달하는 경우 (귀찮은 이유를 물을 수도 있지만) 호출하거나 역 참조 할 수 있습니다. 그러나 Binder를 통해 다른 컨텍스트로 전달하면 리터럴 값은 임의의 숫자가됩니다 (사실 이러한 임의의 숫자는 시작부터 순차적으로 계산됩니다). Binder가 다시 원래 핸들로 변환하여 다시 유용하게 만들도록 원래 컨텍스트로 다시 전달할 때까지 아무것도 할 수 없지만 추적 할 수 있습니다.

3) 파일 설명자 또는 특정 OS / 플랫폼 개체에 대한 참조와 같은 매직 핸들을 전달할 수 있으며 올바른 플래그를 설정하면 Binder는 수신자에 대해 동일한 리소스를 가리키는 복제본을 생성하여 실제로 사용할 수 있습니다. 다른 쪽 끝. 그러나 이것은 매우 적은 유형의 객체에만 적용됩니다.

대부분의 경우 다른 쪽 끝이이를 추적하고 나중에 다시 제공 할 수 있도록 클래스를 전달하거나 직렬화 된 구성 데이터에서 복제본을 생성 할 수있는 컨텍스트로 전달합니다. 작동하지 않을 일을 시도하고 있으며 전체 접근 방식을 재고해야합니다.


1
투어 답장에 감사드립니다. 당신의 권리, 내가해야 할 일은 객체 목록의 참조를 나의 새로운 활동에 전달하는 것입니다. 새 활동은 목록에서 일부 데이터를 가져와 선택 가능한 ListView를 표시합니다. onSelect, 활동은 결과 (클릭 개체와 관련된 일부 데이터)를 호스트 활동에 반환합니다. 내가 올바르게 이해했다면, 옵션 2가이를 가장 적절하게 처리한다고 생각합니다. 이 불투명 핸들을 어떻게 얻습니까?
ahodder 2010

다른 활동은 표시 할 불투명 한 개체에서 데이터를 추출 할 수 없습니다. 아마도 당신이 원하는 것은 표시 될 정보의 사본을 포함하는 지원되는 유형의 일부 대리 객체를 생성하고 전달하는 것입니다.
Chris Stratton

158

Gson을 사용하여 객체를 JSONObject로 변환하고 번들로 전달할 수도 있습니다. 제게는 이것을 할 수있는 가장 우아한 방법이었습니다. 성능에 미치는 영향을 테스트하지 않았습니다.

초기 활동에서

Intent activity = new Intent(MyActivity.this,NextActivity.class);
activity.putExtra("myObject", new Gson().toJson(myobject));
startActivity(activity);

다음 활동에서

String jsonMyObject;
Bundle extras = getIntent().getExtras();
if (extras != null) {
   jsonMyObject = extras.getString("myObject");
}
MyObject myObject = new Gson().fromJson(jsonMyObject, MyObject.class);

3
활동간에 물건을 전달하는 문제이기 때문에 앱의 전반적인 성능에 큰 영향을 미칠만큼 자주 발생하지 않습니다. 그것은 소켓 연결 및 기타 유사한 클래스가있는 것처럼 들리기 때문에 원래 게시물의 DataManager를 직렬화하는 것이 작동하지 않을 것이라고 생각합니다.
britzl 2013

4
또한 구글은 대신 직렬화의이 솔루션을 제안한다 :의 "대안 권장"마지막 섹션 체크 아웃 이 doc 페이지
TechNyquist

3
경고의 한마디로이 기술을 한동안 따랐지만 문자열로 전달할 수있는 것에 대한 메모리 제한이 있으므로 데이터가 너무 크지 않은지 확인하십시오.
jiduvah

프로젝트에 Gson 지원을 추가하는 방법을 알아 보려면 blog.madadipouya.com/2015/09/21/… 을 참조하십시오 .
geekQ

스마트 솔루션, 당신에게 모자를 씌우십시오!
Rohit Mandiwal

20

Parcelable 인터페이스는 의도를 가진 개체를 전달할 수있는 좋은 방법입니다.

사용자 지정 개체를 Parcelable로 만들려면 어떻게해야합니까? 사용 방법에 대한 꽤 좋은 대답입니다.Parcelable

공식 Google 문서 에는 예제도 포함되어 있습니다.


1
또는 직렬화 할 수도 있습니다.
Jeffrey Blattman 2013-08-13

1
하지만 성능이 10 배 크게 감소 !! 이 벤치 마크를 체크 아웃 : developerphil.com/parcelable-vs-serializable
saiyancoder

2
+1 @Mati의 주석, 그러나 단일 객체에 적용될 때 컨텍스트 10x에 넣는 것은 1ms에 해당합니다. 그래서 아마도 그것이 들리는 것만 큼 나쁘지는 않을 것입니다.
pinoyyid

1
동의하다. 문제는 컬렉션을 다룰 때인 데, 이는 Rest API에서 리소스를 얻는 경우 매우 일반적인 사용 사례입니다. 그러나 하나의 물체에 대해 악명 높은 것이어서는 안됩니다. 어쨌든, 모든 상용구 코드가 당신의 방식을 방해한다면, 당신을 위해 모든 것을 생성하는이 lib를 시도 할 수 있습니다 : github.com/johncarl81/parceler . 정말 좋은 접근 방식입니다!
saiyancoder

끊어진 링크 : 404 (찾을 수 없음)
Gallal 2014 년

14

글로벌 애플리케이션을 사용할 수 있습니다. 상태를 .

최신 정보:

이를 사용자 정의한 다음 AndroidManifest.xml에 추가하십시오.

<application android:label="@string/app_name" android:debuggable="true" android:name=".CustomApplication"

그런 다음 프로젝트에 다음과 같은 클래스가 있습니다.

package com.example;

import android.app.Application;

public class CustomApplication extends Application {
    public int someVariable = -1;
}

그리고 " 모든 활동 또는 서비스에서 getApplication ()을 통해 액세스 할 수 있기 때문에 다음과 같이 사용합니다.

CustomApplication application = (CustomApplication)getApplication();
application.someVariable = 123; 

도움이되기를 바랍니다.


1
답장을 보내 주셔서 감사합니다.하지만 어떻게?
ahodder 2010

나는 당신이 Application을 하위 클래스로 만들고 당신이 좋아하는 것을 저장할 수 있다고 믿습니다. 필요한 xml 변경 사항은 위 링크에 언급되어 있습니다.
Mark Storer

9
일반적인 디자인 원칙으로서 실제로 필요하지 않는 한 전역을 피하는 것이 좋습니다. 이 경우 좋은 대안이 있습니다.
dhaag23 2010

좋아, 나는 당신이 말하는 것을 이해한다고 생각합니다. Application을 확장하고 전달해야하는 객체를 포함하는 변수를 던져 넣으십시오. 참조 페이지를 검토했지만 필요한 xml 변경 사항을 보지 못했습니다.
ahodder 2010

이것도 답으로 쓰고 싶었습니다. 이것은 확실히 그것을하는 방법 중 하나입니다. 그러나 이러한 객체는 굴절을 해제하지 않는 한 (또는 앱 컨텍스트가 파괴되지 않는 한) 메모리에 남아 있으며 필요하지 않을 때 공간을 차지할 수 있습니다.
Igor Čordaš

12

객체를 Serializable로 만들고 Bundle의 getSerializableputSerializable 메소드를 사용할 수도 있습니다.


1
나는 그것을 시도했고 그것이 비실용적이라는 것을 금방 깨달았습니다. 전달 된 클래스 (스레드)에 저장된 대부분의 개체가 직렬화 가능하다고 생각하지 않습니다. :) 그래도 고마워.
ahodder 2010

10

가능한 해결책:

Bundle bundle = new Bundle();
bundle.putSerializable("key", new CustomObject());

CustomObject 클래스 :

class CustomObject implements Serializable{
 private SubCustomObject1 sc1;
 private SubCustomObject2 sc2;
}

하위 사용자 지정 개체 :

class SubCustomObject1 implements Serializable{ }

class SubCustomObject2  implements Serializable{ }

7

번들을 통해 개체를 보내는 또 다른 방법은 bundle.putByteArray
샘플 코드를 사용하는 것입니다.

public class DataBean implements Serializable {
private Date currentTime;

public setDate() {
    currentTime = Calendar.getInstance().getTime();
 }

public Date getCurrentTime() {
    return currentTime;
 }
}

DataBean의 객체를 Bundle에 넣습니다.

class FirstClass{
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//When you want to start new Activity...
Intent dataIntent =new Intent(FirstClass.this, SecondClass.class);
            Bundle dataBundle=new Bundle();
            DataBean dataObj=new DataBean();
            dataObj.setDate();
            try {
                dataBundle.putByteArray("Obj_byte_array", object2Bytes(dataObj));

            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();

            }

            dataIntent.putExtras(dataBundle);

            startActivity(dataIntent);
}

객체를 바이트 배열로 변환

/**
 * Converting objects to byte arrays
 */
static public byte[] object2Bytes( Object o ) throws IOException {
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream( baos );
      oos.writeObject( o );
      return baos.toByteArray();
    }

번들에서 개체 가져 오기 :

class SecondClass{
DataBean dataBean;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Your code...

//Get Info from Bundle...
    Bundle infoBundle=getIntent().getExtras();
    try {
        dataBean = (DataBean)bytes2Object(infoBundle.getByteArray("Obj_byte_array"));
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

바이트 배열에서 객체를 가져 오는 방법 :

/**
 * Converting byte arrays to objects
 */
static public Object bytes2Object( byte raw[] )
        throws IOException, ClassNotFoundException {
      ByteArrayInputStream bais = new ByteArrayInputStream( raw );
      ObjectInputStream ois = new ObjectInputStream( bais );
      Object o = ois.readObject();
      return o;
    }

이것이 다른 친구들에게 도움이되기를 바랍니다.


코드를 보면 부드럽고 쉽게 보입니다. 그러나 SDK가 객체를 전달하기 위해 이와 같은 것을 제공하지 않는 이유에 대해 더 많은 것이 있다고 생각합니다. 이 솔루션에 대해 더 자세히 설명해 주시겠습니까?
Mario Lenci 2013

3
모든 코드가 필요하지 않습니다! bundle.putSerializable (objectImplementingSerializable) 사용-여기에서 다시 구현하는 작업 아래에서 수행됩니다 ...
Risadinha

3

1. 매우 직접적이고 사용하기 쉬운 예제로 전달 될 객체가 Serializable을 구현하도록합니다.

class Object implements Serializable{
    String firstName;
   String lastName;
}

2. 번들 객체 전달

Bundle bundle = new Bundle();
Object Object = new Object();
bundle.putSerializable("object", object);

3. Serializable로 번들에서 객체를 전달한 다음 Object로 캐스트합니다.

Object object = (Object) getArguments().getSerializable("object");

0

이것은 내 질문에 대한 매우 뒤늦은 대답이지만 계속 관심을 끌기 때문에 해결해야한다고 생각합니다. 이 답변의 대부분은 정확하며 작업을 완벽하게 처리합니다. 그러나 응용 프로그램의 요구 사항에 따라 다릅니다. 이 답변은이 문제에 대한 두 가지 해결책을 설명하는 데 사용됩니다.

신청

첫 번째는 여기에서 답변에 대해 가장 많이 언급 된 Application 입니다. 응용 프로그램은 컨텍스트에 대한 참조가 필요한 엔터티를 배치하는 데 좋은 개체입니다. `ServerSocket`에는 의심 할 여지없이 컨텍스트가 필요합니다 (파일 I / o 또는 간단한`ListAdapter` 업데이트 용). 저는 개인적으로이 경로를 선호합니다. 나는 응용 프로그램을 좋아하고 컨텍스트 검색에 유용하며 (정적으로 만들 수 있고 메모리 누수를 일으킬 가능성이 없기 때문에) 간단한 수명주기를 갖습니다.

서비스

서비스 ` 는 두 번째입니다. '서비스'는 서비스가 수행하도록 설계된 것이기 때문에 실제로 내 문제에 대해 더 나은 선택입니다.
서비스는 다음에서 장기 실행 작업을 수행 할 수있는 응용 프로그램 구성 요소입니다.
배경이며 사용자 인터페이스를 제공하지 않습니다.
서비스는보다 쉽게 ​​제어 할 수있는보다 정의 된 수명주기를 가지고 있다는 점에서 깔끔합니다. 또한 필요한 경우 서비스를 응용 프로그램의 외부에서 실행할 수 있습니다 (예 : 부팅시). 이는 일부 앱 또는 깔끔한 기능에 필요할 수 있습니다.

이것은 둘 중 하나에 대한 완전한 설명은 아니지만 더 조사하고 싶은 사람들을 위해 문서 링크를 남겼습니다. 전반적으로 ServiceSPP 장치에 ServerSocket을 실행하는 데 필요한 인스턴스가 더 좋습니다.


0

Date 객체를 전달하는 방법을 찾고있을 때이 질문을 보았습니다. 제 경우에는 답변 중 제안 된 것처럼 Bundle.putSerializable ()을 사용했지만 원래 게시물에서 설명한 DataManager처럼 복잡한 작업에는 작동하지 않습니다.

DataManager를 Application에 넣거나 Singleton으로 만드는 것과 매우 유사한 결과를 제공하는 내 제안은 Dependency Injection을 사용하고 DataManager를 Singleton 범위에 바인딩하고 필요한 곳에 DataManager를 삽입하는 것입니다. 증가 된 테스트 가능성의 이점을 얻을 수있을뿐만 아니라 "클래스와 활동간에 종속성을 전달"하는 모든 보일러 플레이트없이 더 깨끗한 코드를 얻을 수 있습니다. (Robo) Guice 는 작업하기가 매우 쉽고 새로운 Dagger 프레임 워크도 유망 해 보입니다.


1
글쎄, 날짜와 같은 것을 사용하면 긴 값을 전달할 수 있습니다. 그러나 나머지는 좋은 것 같습니다. 감사.
ahodder 2013

0

번들을 사용하여 객체를 전달하는 또 다른 간단한 방법 :

  • 클래스 개체에서 키가있는 정적 목록 또는 다른 데이터 구조를 만듭니다.
  • 객체를 생성 할 때 키와 함께 목록 / 데이터 구조에 넣습니다 (예 : 객체 생성시 긴 타임 스탬프).
  • 목록에서 객체를 가져 오기 위해 static getObject (long key) 메서드를 만듭니다.
  • 번들에서 키를 전달하므로 나중에 코드의 다른 지점에서 객체를 가져올 수 있습니다.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.