반응형

Activity간의 Object 공유 - Parcelable

 

하나의 Activity에서 다른 Activity로 데이터를 전달해야 하는 경우에

데이터를 전달하는 Activity에서는 Intent Class의 putExtra() 메소드를 이용해서 데이터를 세팅하고,

받는 쪽에서는 getExtra() 메소드를 이용해서 받게 된다.

 

puExtra()을 이용해서 전달할수 있는 데이터 타입은 대략 다음과 같다.

 

 

ActivityA에서 Book Class의 인스턴스를 관리하고,

ActivityB에서 Book Class의 정보를 UI에 표시하는 상황을 가정해 보자.

 

Book Class는 아래와 같다.

public class Book {
    int serial;
    String isbn;
    String title;
    Bitmap image;
}

 

Book Class의 전체 프라퍼티를 ActivityA에서 ActivityB로 전달하려면 아래와 같이, 프라퍼티 별로 하나씩 put()하고 get()할 수 밖에 없다.

MainActivity

intent.putExtra("serial", book.getSerial());
intent.putExtra("isbn", book.getIsbn());
intent.putExtra("title", book.getTitle());
intent.putExtra("image", book.getImage());
BookActivity

int serial = intent.getIntExtra("serial", 0);
String isbn = intent.getStringExtra("isbn");
String title = intent.getStringExtra("title");
Bitmap image = intent.getParcelableExtra("image");

 

아래와 같이 사용하고 싶은 생각이 들것이다.

MainActivity

Book book = new Book(1, "1-1-1-1", "슬럼독 밀리어네어", bitmap);

Intent intent = new Intent(getApplicationContext(), BookActivity.class);
intent.putExtra("book", book);
BookActivity

Book book = intent.getExtra("book");

 

우선은 Intent  putExtra() 메소스 중, 아래의 두가지 함수원형을 눈여겨 보자!

putExtra( String name, Parcelable value );
putExtra( String name, Serializable value);

우리가 전달하고자 하는 Class가 Serializable 하거나 Parcelable 하게 만들 수 있다면 가능할것 같지 않은가?

 

구글링을 하다 보니 도착한 Goolgle의 가이드를 보면( 링크를 잃어 버렸다),

Serializable 보다는 Parcelable을 권장하는 듯 하니 Parcelable 을 사용하는 방법을 확인해 보기로 하자.

(Serializable의 Java의 표준 인터페이스이며, Parcelable의 안드로이드 SDK에 포함된 인터페이스 이다)

 

Parcelable은 IPC를 이용하여 데이터를 공유하며 Serialiable과는 달리 리플렉션을 사용하지 않으므로

성능면에서 유리하다고 한다.

반면 전달하는 데이터의 해석코드를 필수적으로 작성해야 하므로, 유지보수 측면에서는 상대적으로 귀찮고

번거로운 작업이 동반된다.

 

개략적으로 개념을 잡아 보자면

여러 Activity가 접근할 수 있는 영역(아마도 커널 메모리)에 

1. 전달하고자하는 데이터를 순차적으로 저장 한다.

2. 저장된 데이터를 순차적으로 읽어서 해석할 수 있는 방법을 제공한다.

* 다만 데이터를 쓰고, 읽는 방법은 Parcelable Interface가 지원하는 메소드를 통해서만 가능하다.

* 쓰여진 데이터를 해석하는데 있어 데이터가 쓰여진 순서대로 읽어야 복원이 가능하기 때문에

Write한 순서와 Read한 순서를 일치시키는 것은 반드시 지켜져야한다.

* 개념적으로 직렬화/마샬링과 유사한 개념으로 생각하면 이해가 편할것 같다.

직렬화(serialization)

마샬링(Marshalling)

 

구현 방법은 다음과 같다.

1. 공유하고자 하는 Class가 Parcelable Interface를 상속받도록 구현한다.

public class Book implements Parcelable {
    int serial;
    String isbn;
    String title;
    Bitmap image;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
    }
}

 

2. 필수 추상 메소스를 구현한다.

* writeToParcel() 메소드가 전달할 데이터를 기록하는 메소드의 구현이다.

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(serial);
        dest.writeString(isbn);
        dest.writeString(title);
        dest.writeParcelable(image, flags);
    }

 

3. CREATOR를 구현한다.

public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        public Book[] newArray(int size) {
            return new Book[size];
        }
};

 

4. 생성자를 구현한다.

* 단순히 생성자라기 보다는 Parcelable data를 해석할 수 있는 방법의 구현이다.

public Book(Parcel src) {
        readFromParcel(src);
    }

public void readFromParcel(Parcel src) {
    serial = src.readInt();
    isbn = src.readString();
    title = src.readString();
    image = src.readParcelable(Bitmap.class.getClassLoader());
}

public Book(int serial, String isbn, String title, Bitmap image) {
    this.serial = serial;
    this.title = title;
    this.isbn = isbn;
    this.image = image;
}

 

전체 코드는 다음과 같다.

Book.java

( getter/setter의 구현은 필수 적이지 않다 )

package com.tistory.thepassion.pacelable;

import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;

public class Book implements Parcelable {
    int serial;
    String isbn;
    String title;
    Bitmap image;

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(serial);
        dest.writeString(isbn);
        dest.writeString(title);
        dest.writeParcelable(image, flags);
    }

    public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        public Book[] newArray(int size) {
            return new Book[size];
        }
    };

    public Book(Parcel src) {
        readFromParcel(src);
    }

    public void readFromParcel(Parcel src) {
        serial = src.readInt();
        isbn = src.readString();
        title = src.readString();
        image = src.readParcelable(Bitmap.class.getClassLoader());
        // image = Bitmap.CREATOR.createFromParcel(src);
    }

    public Book(int serial, String isbn, String title, Bitmap image) {
        this.serial = serial;
        this.title = title;
        this.isbn = isbn;
        this.image = image;
    }

    public int getSerial() {
        return serial;
    }

    public void setSerial(int serial) {
        this.serial = serial;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(String isbn) {
        this.isbn = isbn;
    }

    public Bitmap getImage() {
        return image;
    }

    public void setImage(Bitmap image) {
        this.image = image;
    }
}

 

MainActivity.java

package com.tistory.thepassion.pacelable;

import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    Book book;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Resources res = getResources(); Drawable d = res.getDrawable(R.drawable.book);
        Bitmap bitmap = ((BitmapDrawable) d).getBitmap();

        book = new Book(1, "1-1-1-1", "슬럼독 밀리어네어", bitmap);

        Intent intent = new Intent(getApplicationContext(), BookActivity.class);
        intent.putExtra("book", book);

        startActivity(intent);
    }
}

 

ActivityB.java

package com.tistory.thepassion.pacelable;

import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.widget.ImageView;
import android.widget.TextView;

public class BookActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book);

        Intent intent = getIntent();

        Book book = intent.getParcelableExtra("book");
        ((TextView) findViewById(R.id.txtSerial)).setText(String.valueOf(book.getSerial()));
        ((TextView) findViewById(R.id.txtIsbn)).setText(book.getIsbn());
        ((TextView) findViewById(R.id.txtTitle)).setText(book.getTitle());
        ((ImageView) findViewById(R.id.imageView)).setImageBitmap(book.getImage());
    }
}

 

 

지금까지 읽어 내려오면서 궁금한 점이 있었는지 모르겠다.

Parcelable을 구현하기 전의 첫번째 예제에서도 Bitmap 객체를 넘겼었고,

마지막의 전체 구현 코드에서도 Bitmap 객체를 전달하고 있다.

 

예상한것처럼 Bitmap Class가 Parcelable을 implement하고 있기 때문이다.

public final class Bitmap implements Parcelable {
    private static final String TAG = "Bitmap";

    ....
    ....
}

 

Parcelable 을 통해 Object를 공유하고자 할때, 프라퍼티로 포함하고 있는 Class 또한

Parcelable 혹은 Serializable로 구현되어야 한다.

(binary data를 뽑아서 저장하고 복원할 수 있도록 한다면 불가능 하다고 할수는 없으니, 꼭 그런것은 아니다.)

 

사실 Bitmap을 포함한 Class를 다른 Activity로 넘기고 싶어 내용을 확인하고 정리하는 것이기는 한데...

Parcelable을 통해 전달할 수 있는 데이터의 사이즈에는 제한이 있다.

1MB 이상의 데이터를 전달하고자 할때 다음과 같은 에러가 발생할 수 있다.

Caused by: android.os.TransactionTooLargeException: data parcel size 1204788 bytes

 

1MB 제한은 구현한 Parcelable객체 하나당이 아닌 Activity가 속한 프로세스 전역에 걸친 한계 용량으로,

Parcelable을 통해 큰 사이즈의 데이터를 전달하는것은 적합하지 않다.

대략 50kb 미만의 데이터 사이즈를 권장하고 있다.

https://developer.android.com/guide/components/activities/parcelables-and-bundles

 

다른 측면에서 생각해보면

android.provider.MediaStore.ACTION_IMAGE_CAPTURE 를 이용해 촬영한 이미지를 얻을때,

onActivityResult 를 통해 섬네일이미지는 획득이 가능하지만, 

풀이미지를 얻기 위해서는 별도의 Uri를 통해야하는 이유를 이해할 수 있다. 

 

결국 bitmap을 전달해야 할 경우, 

Bitmap 객체가 아닌 파일 경로 혹은 대상 bitmap파일을 불러올수 있는 key를 전달하는 방식으로 우회해야 할것같다.

반응형

'안드로이드' 카테고리의 다른 글

Navigation Drawer 의 Layout구조  (2) 2019.02.27
Android resource compilation failed  (3) 2019.02.23
EditText 포커스 문제  (0) 2019.02.13
Android Studio 단축키  (0) 2018.12.21
Android Studio 화면구성  (0) 2018.05.13

+ Recent posts