導入のモチベーション
Androidでは画面間のデータの受け渡しは、一般的には下のようにIntentにkey-valueを渡して行うと思います。
// FooActivity.java
public static final String KEY = "...";
public static final String KEY = "...";
public static final String KEY = "...";
private String stringValue;
private int intValue;
private boolean booleanValue;
public static void launch(...) {
...
intent.putExtra(KEY, stringValue);
intent.putExtra(KEY, intValue);
intent.putExtra(KEY, booleanValue);
...
}
@Override
public void onCreate(Bundle savedInstanceState) {
...
stringValue = getIntent().getStringExtra(KEY);
intValue = getIntent().getIntExtra(KEY);
booleanValue = getIntent().getBooleanExtra(KEY);
...
}
これが地味に面倒で、数年前にRoboGuiceを使っていたときは @InjectExtra
というのがあり、Extraを自動的にbindできて便利だった(それがDIの範疇に含まれるかはさておき)記憶があるので、似たような機能を提供しているライブラリを探していました。
Extra Binding Libraries
| | ビルドツール | コード生成方法 | Extraの渡し方 | シリアライズ | Fragmentサポート |
|:---|:---|:---|:---|:---|:---|:---|
| dart | maven | apt | use generated class | No | Yes |
| IntentBuilder | gradle | apt | use generated class | No | No |
| PrettyBundle | gradle | apt | use generated class | No | Yes |
| AutoBundle | gradle | apt | use generated class | Yes | Yes |
| injectextra | gradle | apt + bytecode weaving | Intent#putExtra | No | No |
好みの問題もあると思うのですが、私はこの中からコードの品質が良くて仕様を満たしている AutoBundle を選びました。いくつか選定したポイントを書きます。
ビルドツール
何か不都合があったときにPull Requestが送ることができるかは重要ですが、mavenはあまり触りたくなかったので必須ではないですがgradleを使っている方が都合が良かったです。
コード生成方法
どのライブラリもコード生成を行っているのですが、aptで実現できそうなコードでbytecode weavingをするのはメンテすることを考えるとちょっとやりすぎかなという気がします。
Extraの渡し方
Extraを渡される方のクラスはどのライブラリでも bind
で値を代入するのですが、渡す側でもできればキーをどこかに書いたりしたくないので Intent#putExtra
でなく自動生成して補完が効いたりすると嬉しいですね。たとえば以下のようなクラスだと
// FooActivity.java
public static final String KEY = "...";
public static final String KEY = "...";
public static final String KEY = "...";
private String stringValue;
private int intValue;
private boolean booleanValue;
public static void launch(...) {
...
intent.putExtra(KEY, stringValue);
intent.putExtra(KEY, intValue);
intent.putExtra(KEY, booleanValue);
...
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
stringValue = getIntent().getStringExtra(KEY);
intValue = getIntent().getIntExtra(KEY);
booleanValue = getIntent().getBooleanExtra(KEY);
...
}
AutoBundleでは @AutoBundleField
を付けることで #{class_name}AutoBundle
クラスが生成されます。必須のパラメータはコンストラクタに、必須でないパラメータはビルダーのメソッドに渡すことができます。
// FooActivity.java
@AutoBundleField String stringValue;
@AutoBundleField int intValue;
@AutoBundleField(required = false) boolean booleanValue;
public static void launch(...) {
...
FooActivityAutoBundle.createIntentBuilder(stringValue, intValue)
.booleanValue(booleanValue)
.build(context);
...
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FooActivityAutoBundle.bind(this, getIntent());
...
}
シリアライズ
たとえばUserオブジェクトを渡すのに対応している型にシリアライズして、onCreateの中でデシリアライズしてとかだと面倒なので、自動で変換を行ってくれると嬉しいです。AutoBundleでは @AutoBundleField(converter = *.class)
で変換を行うクラスを指定することができます。
@AutoBundleField(converter = UserConveter.class)
Fragmentサポート
Fragment用のbindingライブラリもありますが(sockeqwe/fragmentargs: Annotation Processor for setting arguments in android fragments)、できれば一つのライブラリでActivityとFragmentを統一的に扱えた方が学習コストが低くなって嬉しいです。
というわけで私はAutoBundleを使っています。