Android
Kotlin
DataBinding

Kotlin + Dagger2 + Orma + Databinding + Android Studio 2.1 に嵌まる

More than 3 years have passed since last update.

めちゃくちゃ嵌ったので記録しておきます。まとまらないけど。

あと、Kotlinはあまり関係ないかも。


TL;DR


  • dependenciesに書く順番が大事

  • Dagger 2.2以降とOrmaを使う場合はcom.google.dagger:dagger-compiler:2.xより前に'com.squareup:javapoet:1.6.1'をapt/kaptに指定する

  • Kotlin + databinding + Dagger2 + Android Studio 2.1 Preview 5 以降の組み合わせのとき、com.google.dagger:dagger-compiler:2.x'com.android.databinding:compiler:2.1.0-[preview|beta]x'より前に'com.google.guava:guava:[18.0|19.0]'をapt/kaptに指定する


事の始まり

以下の構成となっているプロジェクトで作業していました。


  • Android Studio 2.1 Preview 4 (com.android.tools.build:gradle:2.1.0-alpha4)

  • Kotlin 1.0.1-2

  • Databinding

  • Dagger 2.0.2

  • Orma 2.4.0

Android Studio 2.1 Preview 5 がリリースされたタイミングで作業環境もアップデート。

すると、エラーが出てコンパイルできなくなりました。

java.lang.NoSuchMethodError:com.google.common.collect.FluentIterable.append(Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable;

Android Studio 2.1 Beta 3がリリースされるまで修正を期待して待ったのですが解決される気配がないので調べました。


Dagger 2.0.2 + Android Studio 2.1 Preview 5 or after

java.lang.NoSuchMethodError:com.google.common.collect.FluentIterable.append(Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable;

見つからないメソッドはGuava 18.0から

  /**

* Returns a fluent iterable whose iterators traverse first the elements of this fluent iterable,
* followed by those of {@code other}. The iterators are not polled until necessary.
*
* <p>The returned iterable's {@code Iterator} supports {@code remove()} when the corresponding
* {@code Iterator} supports it.
*
* @since 18.0
*/

@Beta
@CheckReturnValue
public final FluentIterable<E> append(Iterable<? extends E> other) {
return from(Iterables.concat(iterable, other));
}

このissueによるとDaggerで使われているGuavaは18.0だが、それより下のバージョンが使われているらしい。

なるほど?

$ ./gradlew app:dependencies --configuration=kapt

kapt
+--- org.jetbrains.kotlin:kotlin-annotation-processing:1.0.1-2
| \--- org.jetbrains.kotlin:kotlin-stdlib:1.0.1-2
| \--- org.jetbrains.kotlin:kotlin-runtime:1.0.1-2
+--- com.android.databinding:compiler:2.1.0-beta3
| +--- com.android.databinding:baseLibrary:2.1.0-beta3
| \--- com.googlecode.juniversalchardet:juniversalchardet:1.0.3
+--- com.github.gfx.android.orma:orma-processor:2.4.0
| +--- com.github.gfx.android.orma:orma-annotations:2.4.0
| \--- com.squareup:javapoet:1.6.1
+--- com.google.dagger:dagger-compiler:2.0.2
| +--- com.google.dagger:dagger:2.0.2
| | \--- javax.inject:javax.inject:1
| +--- com.google.dagger:dagger-producers:2.0-beta
| | +--- com.google.dagger:dagger:2.0 -> 2.0.2 (*)
| | \--- com.google.guava:guava:18.0
| \--- com.google.guava:guava:18.0
(以下省略)

(*) - dependencies omitted (listed previously)

確かにDaggerが依存しているGuavaは18.0だ。

$ ./gradlew app:assembleDebug --debug | grep guava > temp.txt

01:24:12.662 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder] Visiting dependency com.android.tools.build:gradle-api:2.1.0-beta3(runtime) -> com.google.guava:guava:17.0(runtime)
01:24:12.662 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.resolveengine.graph.DependencyGraphBuilder] Selecting new module version com.google.guava:guava:17.0
01:24:12.662 [DEBUG] [org.gradle.api.internal.artifacts.ivyservice.ivyresolve.RepositoryChainComponentMetaDataResolver] Attempting to resolve component for com.google.guava:guava:17.0 using repositories [BintrayJCenter, maven, maven2]
...

どうやら、build tools の gradle plugin が Guava 17.0に依存していて、どういうわけかビルド時にもそちらが使われてしまうらしい。

issueにはDaggerを2.1以上にすれば解決すると書かれているので最新の2.3で試してみることにする。


Dagger 2.3 + Orma 2.4.0

しかし、ビルドエラーになる。

java.lang.NoSuchMethodError: com.squareup.javapoet.TypeSpec.classBuilder(Lcom/squareup/javapoet/ClassName;)Lcom/squareup/javapoet/TypeSpec$Builder;

今度はJavaPoetのメソッドが見つからないらしい...

これについてもissueが上がっていた

DaggerがJavaPoetを内蔵しているせいで古いJavaPoetが使われてしまうらしい。

Orma作者の__gfx__さんもこう言ってた。

ただ、こんなTweetも見つけたので

試しに以下のようにkaptでjavapoetを明示してみた。

dependencies {

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

kapt "com.android.databinding:compiler:2.1.0-beta3"

compile("com.github.gfx.android.orma:orma:2.4.0") {
exclude group: 'com.tunnelvisionlabs', module: 'antlr4-runtime'
exclude group: 'com.tunnelvisionlabs', module: 'antlr4-annotations'
}
kapt "com.github.gfx.android.orma:orma-processor:2.4.0"
kapt 'com.squareup:javapoet:1.6.1'

compile 'com.google.dagger:dagger:2.3'
kapt 'com.google.dagger:dagger-compiler:2.3'
...

すると…

java.lang.NoSuchMethodError: com.google.common.collect.ImmutableSetMultimap$Builder.putAll(Ljava/lang/Iterable;)Lcom/google/common/collect/ImmutableSetMultimap$Builder;

さっきまでとは違うエラーだ...

ちなみに、Dagger 2.2に下げると

java.lang.NoSuchMethodError:com.google.common.collect.FluentIterable.append(Ljava/lang/Iterable;)Lcom/google/common/collect/FluentIterable;

最初の問題、解決してなかったじゃんか...


Dagger 2.3 + Databinding

(2016/04/21追記)

このエラーが出る原因についてはこちらのコメント参照

(追記ここまで)

こちらの記事(AndroidでKotlinを使ったDagger2)にもあるように、ImmutableSetMultimap$Builder.putAllが見つからないのはDagger 2.3からのようです。

issueのコメントによると、dagger-compilerより先にguavaを明示するとよいとのこと。

ただ、自分の構成ではkaptのdatabindingより先に明示しないと動きませんでした。

dependencies {

compile "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion"

kapt 'com.google.guava:guava:19.0'
kapt "com.android.databinding:compiler:2.1.0-beta3"

compile("com.github.gfx.android.orma:orma:2.4.0") {
exclude group: 'com.tunnelvisionlabs', module: 'antlr4-runtime'
exclude group: 'com.tunnelvisionlabs', module: 'antlr4-annotations'
}
kapt "com.github.gfx.android.orma:orma-processor:2.4.0"
kapt 'com.squareup:javapoet:1.6.1'

compile 'com.google.dagger:dagger:2.3'
kapt 'com.google.dagger:dagger-compiler:2.3'
...
}

ここまでやってついにビルドが成功しました...

この状態からDaggerを2.2や2.0.2にダウングレードしても問題なくビルドできます。今までの苦労はいったい...


JavaPoetの指定場所

ビルドには成功したのですが、dependenciesに書く順番によって有効になったりならなかったりするのが割と衝撃でした。

そして、気になってDaggerとOrmaの問題で追加したJavaPoetの指定をdagger-compilerより後ろにしたらどうなるのか試してみると...

java.lang.NoSuchMethodError: com.squareup.javapoet.TypeSpec.classBuilder(Lcom/squareup/javapoet/ClassName;)Lcom/squareup/javapoet/TypeSpec$Builder;

やはりこちらも指定の順番が重要なようです。

消耗した...


おわりに

dependenciesに書く順番が結果に影響するのが意外すぎました。aptこわい。