- Android Studio にて、コードを複数のプロジェクトから再利用できるようにライブラリ化して、別プロジェクトから利用したい。
ライブラリプロジェクトの作り方
ざっと調べた限りでは、Android Studio ではライブラリだけのプロジェクトというのは作れないみたい。普通にアプリとしてプロジェクトを作って、その中でライブラリにしたい部分を別モジュールに分ける。
まず普通に Empty Activity とかで新しいプロジェクトを作成する。プロジェクトにはデフォルトのモジュールとして app モジュールが作られる。アクティビティなしで作っても app モジュールは必ず作られるので、動作テストアプリとして使えばよろし。
File > New > New Module で Android Library を作成すると、ライブラリモジュールの雛形が作られる。
app モジュールからライブラリモジュール内のクラスなどを使用するには、app モジュールの build.gradle を編集し、
dependencies
に以下の一行を追加。Android Studio 3 から記法が変わった ので注意。// Android Studio 3.x の場合 implementation project(':ライブラリモジュール名') // Android Studio 2.x を使っている場合は 以下のように compile project(':ライブラリモジュール名')
これで、アプリとライブラリを分離できたので、再利用したいコードやリソースはライブラリモジュール側に書いていけばOK。
別のプロジェクトのライブラリモジュールを利用
上の手順で再利用したいコードの分離はできたので、次は他のプロジェクトからライブラリモジュールを利用する手順。
ここでは例として、以下のようなフォルダ構成で、MyApplication プロジェクトから、MyLib プロジェクトの testlib モジュールを利用したいとする。
+-[MyLib] <ライブラリプロジェクト
| +-[app]
| +-[testlib] <ライブラリモジュール
|
+-[MyApplication] <別のプロジェクト
+-[app] <ここから testlib を利用したい
MyApplication 側の setting.gradle を編集。
incluede
に testlib を追加して、testlib の参照先フォルダを指示する。この時点で、MyApplication プロジェクトに testlib モジュールが表示される。include ':app', ':testlib' project(':testlib').projectDir = new File(settingsDir, '../MyLib/testlib')
上記の場合、
settingsDir
(setting.gradle のフォルダ) からの相対パスでの指定。絶対パスでもいいけど、共同作業で困るので普通はやらない。あとは、MyApplication 側の app/build.gradle を編集して、
dependencies
にライブラリモジュールを追加するだけ。// Android Studio 3.x の場合 implementation project(':testlib') // Android Studio 2.x の場合 compile project(':testlib')
もしこの時点で 「
Unable to resolve dependency for ...
」 などとエラーになる場合は、アプリ側とライブラリモジュール側の Build Variants の構成が違うためうまく連動できない状態なので、詳しくは 次の章 を参照。
これで、MyApplication から testlib のクラスやリソースの利用はもちろん、まるで同じプロジェクトかのように testlib 側のソースも修正できるし、連動してビルドされるようになる。
アプリとライブラリの Build Variant の連動
Android Studio 3.x からは、implementation project()
で追加したモジュールについては、自動的にアプリ側と同じ Build Variant で連動してビルドされる。Android Studio 2.x では自動的にやってくれなかった(後述)ので嬉しい。
ただ、この 「アプリ側と同じ Build Variant」 というのが曲者で、アプリ側とライブラリ側で buildTypes
や productFlavors
の構成が一致しない場合、「依存関係が解決できない!」とか怒られる。
解決できない状況と対応策についてはざっくり以下。
アプリ側で使っている buildTypes
の一部が、ライブラリ側には存在しない
例えばアプリ側の buildTypes
が {debug, beta, release}
で、ライブラリ側には {debug, release}
しかない場合、beta
でビルド時の対応先が不明。
この場合は matchingFallbacks
を使ってライブラリ側の対応先を指示。
buildTypes {
debug {
}
beta {
// ライブラリ側に beta がない場合は代わりに release を使うよう指示
// 優先度順に複数指定可
matchingFallbacks = ['release']
}
release {
}
}
アプリ側とライブラリ側で同名の dimension を使っていて、その dimension のフレーバーの一部がライブラリ側には存在しない
そもそも dimension て何?って人は 別記事参照。
buildTypes
が productFlavors
になるだけで、状況としては上のケースと同じ。対応先がないアプリ側のフレーバーに matchingFallbacks
を記述して、ライブラリ側のフレーバーを指示。
ライブラリ側にだけ存在する dimension のフレーバーがある
その dimension についてはどのフレーバーを使用すべきか、アプリ側の build.gradle に missingDimensionStrategy
を使って指示。
defaultConfig {
// ライブラリ側にだけ存在する dimension について、どのフレーバーを
// 使用すべきか指示。フレーバーは優先度順に複数指定可。
// defaultConfig に書いた場合は全フレーバーで共通設定。
missingDimensionStrategy 'dimension名' 'フレーバー1' 'フレーバー2' ...
}
productFlavors {
trial {
// フレーバーごとに指示する事も可能
missingDimensionStrategy 'dimension名' 'フレーバー1' ...
}
}
オマケ:Android Studio 2.x までの手法
Android Studio 2.x の頃は、アプリ側とライブラリ側の Build Variant は連動してなくて、ライブラリモジュール側はデフォルトでは常に release ビルド されていたので、連動させたければ以下のように設定する必要があった。
ライブラリモジュールの build.gradle を編集。
publishNonDefault
を true に設定する1行を追加。この設定がないと利用側からはデフォルトのビルド設定 (release) のモジュールしか見えないが、これで全てのビルド設定のモジュールを扱えるようになる。android { ... publishNonDefault true ... }
利用側の app/build.gradle を編集。今まで
dependencies
内でcompile project(':testlib')
などと書いていた場所を、Build Variant ごとに別処理をするように書き換える。
debugCompile project(path: ':testlib', configuration: 'debug') releaseCompile project(path: ':testlib', configuration: 'release')
これで、Build Variant が debug の時はライブラリは debug、release の時は release でビルドされるようになる。今後 buildTypes
を増やした場合も、その時はどっちでビルドすべきか個別で指示できる。
BuildConfig はアプリとライブラリで別
debug ビルドと release ビルドで処理を変えたい場合、
if ( BuildConfig.DEBUG )
{
// debug 時の処理
}
などと書くが、この BuildConfig
はモジュールごとに別 に作られてるので注意。
ソース内で BuildConfig
を初めて使う場合、エディタが気をつかって自動で import してくれるが、よく見ずうっかり関係ないモジュールの BuildConfig
をインポートしてしまうと、以後 debug ビルドなら動くはずのコードが動かなかったり、謎の現象に悩まされる。
アプリモジュールとライブラリモジュールのリソースの関係
ライブラリモジュールを作成すると、ライブラリ側にも AndroidManifest.xml やリソース用のフォルダが自動的に作られるが、マニフェストの各種設定やリソースIDはライブラリ側とアプリ側で独立してる訳じゃなくて、ビルド時に無理矢理マージしてるだけ なので、それを意識して使わないと意図せぬ事態が起きる。以下は特に注意。
リソースIDはアプリ側の値で上書きされる
もしライブラリとアプリで 同じIDのリソースがある場合、アプリ側の値で上書きされる ので、ライブラリ側のIDには全てライブラリ名のプレフィックスをつけるなり、偶然かぶらないように工夫する。
逆に、意図的にアプリ側から上書きさせる事で、ライブラリ内のレイアウトのマージン値などをアプリ側から微調整したりもできるので、何事も使いよう。
ライブラリ側の AndroidManifest.xml に uses-permission
とか書かない
例えばライブラリにネットやら録音やらいろんなヘルパークラスを詰め込んだとして、ライブラリ側の AndroidManifest.xml にそれらの機能を使うために必要な uses-permission
とかを書いてしまうと、もし利用側のアプリでその機能を使ってなくても問答無用でパーミッションが付いてしまう。
パーミッション指定などは、アプリ側の AndroidManifest.xml にだけ書くべき。