Help us understand the problem. What is going on with this article?

Parcelableを使う話

More than 1 year has passed since last update.

Androidで、ActivityやFragmentにパラメータを渡すには、Bundleを使うわけですが、そのBundleは基本的な型のデータ以外に、Parcelableインターフェースを実装したクラスのインスタンスも受け付けてくれます。

さて、そのParcelableインターフェースとはどういったものかというと、思いっきり端折って書いてしまえば、

interface Parcelable {
    int describeContents();
    void writeToParcel(Parcel dest, int flags);
}

こんな感じで、これらのメソッドを実装すれば、Bundleに渡すことができる(つまり、ActivityやFragmentに渡すことができる)ということになります。

で、そのメソッドの実装方法ですが、

  1. 自力で書く
  2. コードジェネレータ・プラグインを使う (https://plugins.jetbrains.com/plugin/7332?pr=idea)
  3. アノテーション・ライブラリを使う (https://github.com/johncarl81/parceler)

と言う方法があります。

筆者は自力で書いたことがない(めんどくさいじゃん)ので、ここでは、それ以外の方法を取りあげます。

やってみる

ここでは、

public class A {
    int foo;
    String bar;
    B buzz;
}

public class B {
    int hoge;
    String fuga;
}

public class B2 extends B {
    double piyo;
}

という3つのクラスを使って、

    public static void parcelableTest() {
        A a = new A();
        a.foo = 1;
        a.bar = "2";
        a.buzz = new B();
        a.buzz.hoge = 3;
        a.buzz.fuga = "4";

        Bundle bundle = new Bundle();
        bundle.putParcelable("PARCELABLE", a);

        A a2 = bundle.getParcelable("PARCELABLE");

        Log.d("parcelableTest", 
                "foo: " + a2.foo + "\n" + 
                "bar: " + a2.bar + "\n" +
                "buzz.hoge: " + a2.buzz.hoge + "\n" +
                "buzz.fuga: " + a2.buzz.fuga + "\n"
        );

        B2 b2 = new B2();
        b2.hoge = 5;
        b2.fuga = "6";
        b2.piyo = 7.0;
        a.buzz = b2;

        bundle.putParcelable("PARCELABLE", a);
        a2 = bundle.getParcelable("PARCELABLE");

        if (a2.buzz instanceof B2) {
            b2 = (B2) a2.buzz;

            Log.d("parcelableTest",      
                    "foo: " + a2.foo + "\n" +
                    "bar: " + a2.bar + "\n" +
                    "buzz.hoge: " + b2.hoge + "\n" +
                    "buzz.fuga: " + b2.fuga + "\n" +
                    "buzz.piyo: " + b2.piyo + "\n"
            );
        } else {
            Log.d("parcelableTest",      
                    "foo: " + a2.foo + "\n" +
                    "bar: " + a2.bar + "\n" +
                    "buzz.hoge: " + a2.buzz.hoge + "\n" +
                    "buzz.fuga: " + a2.buzz.fuga + "\n"
            );
        }
    }

というようなコードを実行して、Parcelableの復元を確認します。

コードジェネレータ・プラグイン

AndroidStudioのPreferencesダイアログで、Parcelableをキーワードにして検索すると、「Android Parcelable code generator.」というプラグインが見つかります。

これをインストールした後、上記のA,B,B2クラスのそれぞれを開いたエディタ上で右クリック、「Generate」「Parcelable」を選んでいくと、それぞれのクラスに必要なコードが生成されて、

public class A implements Parcelable {
    int foo;
    String bar;
    B buzz;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.foo);
        dest.writeString(this.bar);
        dest.writeParcelable(this.buzz, 0);
    }

    public A() {
    }

    protected A(Parcel in) {
        this.foo = in.readInt();
        this.bar = in.readString();
        this.buzz = in.readParcelable(B.class.getClassLoader());
    }

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

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

public class B implements Parcelable {
    int hoge;
    String fuga;

    public B() {
    }

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(this.hoge);
        dest.writeString(this.fuga);
    }

    protected B(Parcel in) {
        this.hoge = in.readInt();
        this.fuga = in.readString();
    }

    public static final Creator<B> CREATOR = new Creator<B>() {
        public B createFromParcel(Parcel source) {
            return new B(source);
        }

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

public class B2 extends B {
    double piyo;

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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        super.writeToParcel(dest, flags);
        dest.writeDouble(this.piyo);
    }

    public B2() {
    }

    protected B2(Parcel in) {
        super(in);
        this.piyo = in.readDouble();
    }

    public static final Creator<B2> CREATOR = new Creator<B2>() {
        public B2 createFromParcel(Parcel source) {
            return new B2(source);
        }

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

となります。やっていることは、なんとなくわかりますが、自分で書くのはめんどくさいですね。

先の検証コードを実行したときのログは、

30724-30724/jp.fogg.fragmentanimation.gene D/parcelableTest﹕ foo: 1
    bar: 2
    buzz.hoge: 3
    buzz.fuga: 4
30724-30724/jp.fogg.fragmentanimation.gene D/parcelableTest﹕ foo: 1
    bar: 2
    buzz.hoge: 5
    buzz.fuga: 6
    buzz.piyo: 7.0

となります。ちゃんと復元できてますね。

アノテーション・ライブラリ

アノテーション・ライブラリを使うために、build.gradleに準備をします。
必要なことをまとめてしまうと、

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
    }
}

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile "org.parceler:parceler-api:1.0.3"
    apt "org.parceler:parceler:1.0.3"
}

を追加します。

AndroidStudioが用意してくれるテンプレな環境だと、最初のclasspathはプロジェクト直下のbuild.gradle、それ以外はappの下のbuild.gradleになると思います。

元のA,B,B2クラスの修正は簡単で、

@Parcel
public class A {
    int foo;
    String bar;
    B buzz;

    public A() {}
}

@Parcel
public class B {
    int hoge;
    String fuga;

    public B() {}
}

@Parcel
public class B2 extends B {
    double piyo;

    public B2() {super();}
}

原則としてはクラスに、@Parcelアノテーションを付加するだけです(引数無しのコンストラクタが必須なので、上の例では、明示的に追加しました)。

簡単ですね。

この魔法の肝は、元のクラスをParcelableにするのではなく、Parcelableなラッパーを生成すると言うところです。
そのため、検証コードの

        bundle.putParcelable("PARCELABLE", a);
        a2 = bundle.getParcelable("PARCELABLE");

の部分に修正が必要です。

        bundle.putParcelable("PARCELABLE", Parcels.wrap(a));
        a2 = Parcels.unwrap(bundle.getParcelable("PARCELABLE"));

このようにします。

実行時のログは、こちら。

31654-31654/jp.fogg.fragmentanimation.anno D/parcelableTest﹕ foo: 1
    bar: 2
    buzz.hoge: 3
    buzz.fuga: 4
31654-31654/jp.fogg.fragmentanimation.anno D/parcelableTest﹕ foo: 1
    bar: 2
    buzz.hoge: 5
    buzz.fuga: 6
    buzz.piyo: 7.0

ちゃんとできてますね。

まとめ

  • 文明の利器を活用すれば、Parcelableは怖くない。
  • 偉大な先人の成果に敬意を表しつつ、活用させていただきましょう。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした