
はじめに
こんにちは。 STORES 決済 の Android アプリ/SDK の開発をしている n-seki です。
もう年の瀬ですね!今回の記事ではFused Library プラグインを取り上げようと思います。
STORES 決済 の SDK
Fused Library プラグインの詳細に入る前に STORES 決済 の話をさせてください。弊社ではアプリのみならずSDK(ライブラリ)も公開しています。
このSDKを活用することでクレジットカード決済などのキャッシュレス決済の機能をかんたんにアプリに組み込むことができます。
技術的には、このライブラリは Maven Repository で公開しています。Androidのライブラリの実態は .aar という拡張子のファイルなので、これがネットワーク上に配置されている、というイメージです。
昨今のAndroid開発においてはGradleでマルチモジュール構成とするのが一般的だと思います。 STORES 決済 でも当然のようにGradleマルチモジュール構成なのですが、ライブラリの場合にはちょっとだけ困りごとが発生します。
基本的に、Gradleモジュールとライブラリ(aar)には一対一の関係があります。つまり1つのGradleモジュールから1つのライブラリ(aar)が生成されます。単一のGradleモジュールで構成されたライブラリであれば良いのですが、複数のGradleモジュールで構成されるライブラリでは 依存するすべてのGradleモジュールをライブラリ(aar)として公開する必要があります 。
複数のライブラリの公開が要求されるため、
- 単純に管理が煩雑になる
- 本来は公開したくないモジュールも公開せざるを得ない状況が発生しうる
- Daggerや難読化が絡んでくるとメンテナンスコストが高くなる
などの課題が顕在化しえます。
STORES 決済 でもまさにこのあたりに課題を感じており、長らく解決策を模索していました。
Fat aar という選択
これらの問題は「複数のGradleモジュールから1つのライブラリ(aar)が生成されれば」解決されそうです。 これを実現するためのサードパーティライブラリは以前から存在していました。
fat aar つまり巨大なaarを生成するというアプローチで、これ以外にもいくつかのライブラリがあります。 かねてからライブラリの存在は知っていたのですが、見てみると最近はあまりメンテナンスされていない状況のようでした。
一方で「複数のGradleモジュールから1つのライブラリ(aar)を生成したい」という開発者の声は大きく、Google Issue Tracker にはずばりのIssueがあって、多数vote されていました。
https://issuetracker.google.com/issues/62121508
長らく動きがなかったのですが、今年ついにGradleプラグインがリリースされました。それが Fused Library プラグイン です
Fused Library プラグイン を試す
このプラグインのコンセプトは上述の通り「複数のGradleモジュールから1つのライブラリ(aar)を生成する」というものです。それを実現するため、
- Fused Libraryとして公開するためのGradleモジュールを新規作成する
- 含めるGradleモジュールや依存を
includeで宣言する
という対応が必要です。
上記のAndroid Developerや以下のREADMEに導入手順が詳細に記載されています。
私は以下のようなGradleモジュールの構成のプロジェクトを作成して Fused Library プラグイン を試してみました。
| Gradleモジュール | 概要 |
|---|---|
| app | myfusedlibraryを利用するアプリ |
| myfusedlibrary | Fused Library として公開するモジュール |
| public_lib | ライブラリの公開インターフェイス実装で、internal_libに依存 |
| internal_lib | ライブラリ内部実装 |
internal_libにはこのようなクラスを置いて、
class InternalClass { fun internalSay() { println("Hello!") } }
public_libでは InternalClass を利用するクラスを置きました。
class PublicClass { fun say() { InternalClass().internalSay() } }
そしてmyfusedlibrary/build.gradle.ktsには以下のような記述をします。
plugins {
alias(libs.plugins.android.fusedlibrary)
`maven-publish`
}
androidFusedLibrary {
namespace = "com.example.myFusedLibrary"
minSdk = 30
}
dependencies {
include(project(":public_lib"))
include(project(":internal_lib"))
}
publishing {
publications {
register<MavenPublication>("release") {
groupId = "com.example"
artifactId = "my-fused-library"
version = "1.0"
from(components["fusedLibraryComponent"])
}
}
}
この状態で myFusedLibrary を Local Maven に publish してみます。
./gradlew :myfusedlibrary:publishReleasePublicationToMavenLocal
生成されたaarの中身を見てみると、確かに public_lib, internal_lib 両モジュールの内容が含まれていそうです。

実際に appモジュールでライブラリを依存に含めて PublicClass を参照するとちゃんと動いてくれました。
dependencies {
implementation("com.example:my-fused-library:1.0")
}
@Test fun play() { PublicClass().say() // Hello! }
Fused Library は救いとなるか?
このプラグインを導入することで Maven Repository へのpublish周りのスクリプトや手順は簡略化でき、管理の煩雑さからは開放されそうな期待感があります。
一方で、可視性の制御はやはり難しいのではないかと思っています。どういうことかと言うと、先に課題として挙げた「本来は公開したくないモジュールも公開せざるを得ない状況が発生しうる」点については(いま時点の)Fused Library では解決できていないようだ、ということです。
今回のサンプルプロジェクトにおいて、実は app モジュールで InternalClass も参照できてしまいます。
@Test fun play() { InternalClass().internalSay() // Hello! }
直感的には解消は難しい気もしますし、想定しているユースケースではなくプラグインの責務でもない、とも言えるかもしれません。実際、Fused Libraryは使わずにこれまで通り internal_lib を個別にMaven Repositoryに公開すれば app からの直接の参照は一定回避できます。
しかし、このあたりの可視性も制御できると利用の幅が広がりそうだなと妄想しています。
いずれにせよ Fused Library はまだ試験的な機能なので、これからの発展に期待大です!
さいごに
プロダクションでの検証をしたわけではないですが、Fused Library はライブラリ開発における光明になりうるのではないかと思っています。 数年前から望んでいた機能なのでフィルターがかかっているだけかもしれませんが、これからが非常に楽しみですし、継続的に検証もやっていきたいです。