Gradleの依存関係のimplementation/apiの使い分けについて。
Android公式の説明は、正しく詳細なのだが、中々理解できなかった。
ようやくimplementation/apiの使い分けが腑に落ちたので、まとめる。
最初は、ユースケースで考えた方が分かりやすいと思う。
- ライブラリモジュールの場合
- ライブラリのAPIに使う依存ライブラリ -> "api"
- そうでない依存ライブラリ -> "implementation"
- アプリモジュールの場合1
- "implementation"を使う。"api"でも動作変わらないが、あえて使う理由はない。
この後の説明長いが、ここを抑えておけば、読まなくても害はなさそう。
ケーススタディ
自作ライブラリに、以下のAPIがあるとする。
data class User(val id: String, val name: String)
class UserRepository {
fun getUser(id: Int): LiveData<User> {
...
}
fun add(user: User) { ... }
}
このケースでは、自作ライブラリを使うモジュールをコンパイルする際に、LiveDataのライブラリが必要になる。なので、自作ライブラリの依存関係の設定では"api"を使う2。
自作ライブラリのbuild.gradleの記述例。
dependencies {
api 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
}
もし、UserRepository#getUser()がprivateであれば、自作ライブラリを使うモジュールのコンパイルにLiveDataライブラリは不要である。なので、自作ライブラリの依存関係で"implementation"を指定する。
詳細
アプリ -> 自作ライブラリ -> LiveDataライブラリという方向で依存しているとする。
このときの、自作ライブラリのbuild.gradleの記述を考える。
LiveDataライブラリへの依存関係を"implementation"と"api"のどちらに指定しようが、自作ライブラリにとっては違いはない。自作ライブラリのコンパイル時からLiveDataライブラリは必要とみなされる。一方で、アプリからの見え方には違いが出る。"implementation"を使った場合は、実行時から必要なライブラリと扱われ、"api"を使った場合はコンパイル時から必要という扱いになる。
アプリへの依存関係の見せ方は、自作ライブラリをmavenリポジトリにデプロイしたときのpomの内容で決まる(~/.gradleを探すと外部ライブラリのpom見れる)。pomを見てみると、"implementation"は"runtime" scope、"api"は"compile" scopeに対応していることが分かる。
この記事に、図があって分かりやすい。
https://qiita.com/tkhs0604/items/7afe40599af2354eb2ad
蛇足
"api"の依存関係が推移する理由。
アプリ -> A -> B -> Cと依存していて、AのAPIにB、BのAPIにCを含むとする。
当然、AのAPIにCも含まれるので、アプリのコンパイルにはA, Bだけでなく、Cも必要ということになる。
これが公式の説明で言うところの、依存関係が推移するという意味。
LiveDataを自作ライブラリのクラスやinterfaceでwrapすれば、自作ライブラリからLiveDataへの依存関係に"implementation"が使えるようになる。LiveDataライブラリとその依存ライブラリが、コンパイル時に不要になるので、アプリのビルドは早くなる3。