※解決しました http://sys1yagi.hatenablog.com/entry/2015/06/17/190547
個人アプリにフォイフォイ導入して上手く行ったので仕事の方でも、と思ってたら思わぬ罠で挫折したので記録しておきます。
環境
com.android.tools.build :gradle:1.3.0-beta3
com.android.databinding:dataBinder:1.0-rc0
結論
android gradle plugin
のmultidex処理系の改修を待つ。具体的にはData Binding用に生成したクラス群をmaindexlist.txtに含める様に修正されるのを待つ。or adt-devに参加して直す
問題
ViewPager内のFragmentで利用するArrayAdapterのViewをData Bindingに適用しようとした所、実行時に以下のエラーが発生した。
Could not find class 'android.databinding.ViewDataBinding$IncludedLayoutIndex[][]', referenced from method com.hoge.databinding.XXXBinding.<clinit> java.lang.IllegalAccessError: tried to access class android.databinding.ViewDataBinding$IncludedLayoutIndex[][] from class com.hoge.databinding.XXXBinding
com.hoge.databinding.XXXBinding
はData Bindingによって生成されたクラス。エラーが起こった箇所はAdapter内のXXXBinding.inflate()
の部分。これを実行した時にクラッシュする。
@Override public View getView(int position, View convertView, ViewGroup parent) { if(convertView == null) { XXXBinding binding = XXXBinding .inflate(LayoutInflater.from(getContext())); //... } //...
原因
XXXBinding
クラスはandroid.databinding.ViewDataBinding
クラスを継承している。
public class XXXBinding extends android.databinding.ViewDataBinding { //... }
これらのクラスがmultidexによって分断されてしまうと、この問題が起こる。
classes.dex <- android.databinding.ViewDataBindingが入ってる classes2.dex <- XXXBindingが入ってる
つまり以下の条件を満たすときData Bindingの利用は厳しくなる。
- multidexを使っている (
multiDexEnabled = true
にしている) - multidexの結果classes.dexが分割される (ビルドしたあと
build/intermediates/dex/[flavor]/[variant]/
みると分かる)。multiDexEnabled = true
にしていても65535問題をクリアしていればdexは分割されない。分割されていなければ問題は出ない。 - BindingをFragmentやArrayAdapterで使っている(Activityなら多分問題ない)
この条件を満たしていても動く場合もあるかもしれないけどいつぶっ壊れるか分からないので怖い。
なぜ分断されるのか??
multidexでdex分割を処理する時、前段でprimary dex入りするクラスを推論するフェーズがある。ここではApplicationやActivityの依存関係からアプリ起動時に必要なクラス群を抽出しているぽい。この時android.databinding.ViewDataBinding
はprimary dexに必ず格納されるが、生成されたXXXBinding
はprimary dexから漏れる場合がある。現状のpluginではその辺の条件が足りてないんだと思う。
primary dex入りするクラス群はbuild/intermediates/multi-dex/[flavor]/[variant]/maindexlist.txt
で確認できる。maindexlist.txt
に生成されたBindingクラスが書かれていなければ問題が起こる可能性がある。
対応(暫定)
完全には分かっていないが、primary dex入りするクラスが参照しているクラスもprimary dex入りするらしい。つまりandroid.intent.category.LAUNCHER
なActivity(ここではMainActivityとします)がXXXBindingを参照している場合maindexlist.txt
にXXXBindingが含まれる様になる。という事で以下の様なコードを書くとクラッシュせず動作する様になる。
public class MainActivity extends AppCompatActivity { //この記述をすると動作する static { Class clazz = XXXBinding.class; } }
この対応の問題点
XXXBinding
が増える度に書かなければならないので漏れる可能性がある- Proguardのoptimizeで消される可能性がある
- 書いてもsecondary dex入りする可能性は否定できない
暫定で動くものの総合的には問題が出る条件
を満たす場合Data Bindingを見送る方がよさそう。
試したけどダメだった事
その他いくつか試したけどダメだった事のメモ。
- multidex.keepを書く
- Multi-dex Support を使おう の辺りが参考になる。dexに
--main-dex-list
オプションでprimary dexに入れるクラス群を定義したファイルを渡せる。これを使えばXXXBindingをprimary dexに入れられるのではないかと考えたがここにはファイル一つしか渡せない。既にpluginが推論して生成するmaindexlist.txt
が自動で渡る様になっているので無理。渡したら次はmaindexlist.txt
が無視されるので爆死する。
- Multi-dex Support を使おう の辺りが参考になる。dexに
- XXXFragmentやXXXAdapterでstatic initializer
- XXXActivityじゃなく、FragmentやAdapterでstatic initializerを書いてみたがダメだった。
multiDexEnabled
以外に何かオプションないかな?- なさそう
終わりに
サイコーなDaba Bindingですがこういった落とし穴がありました。大規模なアプリへの適用はまだ厳しそうな印象です。目下は簡単な再現環境をつくって issue tracker にぶち込もうかと思います。グーメン。