Androidでは従来のjar形式の外部ライブラリに加えてAndroid独自形式のaarファイルでも外部ライブラリを使うことが出来ます。aarはjarと同様にzip圧縮されたファイルなのですが各種リソースやnativeライブラリも含むことが出来ます。
作り方や読み込み方は既にたくさん記事が公開されているのでそちらを参照してもらうことにして、今回は自前のnativeライブラリをビルドする際にaar内にあるnativeライブラリとリンクする方法を考えてみました。
何でそんなことすんねん
そんなマニアックなこと誰もしねぇよってのは置いといてんかぁ^^;だって自分はあるんだも〜ん(^^)v
というのも同じnativeライブラリを参照する複数のaar(今自分が実装しようとしているのも含めて)が同じプロジェクト内にあるとapk生成時に「どれを使ったらええかわからんからどないかしてんかぁ」と怒られてしまうのです。
純粋なJavaのクラスだけの外部ライブラリであれば、大抵はgradleによる依存関係の競合解決でまぁまぁなんとかなりますたまになんとかならないことも有るけど(汗) ですがnativeライブラリまでは面倒見てくれません。同じファイル名のライブラリを同じ場所にコピーしようとして怒られてしまいます。
特に厄介なのがstlやC/C++の標準ライブラリ系で、単独のaarとして使う場合には当然含めておかないといけないのですが、nativeライブラリを含むaarを外部ライブラリとして使うプロジェクトで自前のnativeライブラリを作ろうとすると直ぐにこの問題にぶち当たります。
解決策
色々試した結果、
- build.gradleでaarからnativeライブラリを一旦作業ディレクトリにコピー
- 作業ディレクトリ内のnativeライブラリに対して自前のnativeライブラリをビルド
- このままだとaar内と自分のnativeライブラリの成果物の両方に同じnativeライブラリが含まれてしまうので重複するものはexcludeしてコピーする
ってのが一番無難そうでした。
例えばAndroid用のオレオレusbライブラリandrousb.aarをandrousb-releaseモジュールとしてプロジェクト内に含んでいるとします。
もちろんこれは単なる例なのでnativeライブラリを含むaarであれば何でもいいです。
build.gradle内に次の3つのタスクを追加します。
- aarからnativeライブラリを作業ディレクトリにコピーするタスク
- 自前のnativeライブラリをリンクするタスク
- モジュールのビルド用にnativeライブラリをコピーするタスク
aarからnativeライブラリを作業ディレクトリにコピーするタスク
まずは1つめ、aarからnativeライブラリを作業ディレクトリにコピーするタスクです。
御存知の通りaarファイルは従来のjarファイルと同様単にzipで圧縮されているだけなのでzipTreeを使って中身のファイルへアクセスできます。
ここでは{プロジェクト}/{モジュール}/src/main/jni/externalLibsを作業ディレクトリにしています。後は共有ファイルとヘッダファイルを指定するだけでおっけー(๑´ڡ`๑)
1 2 3 4 5 6 7 |
task ndkA(type: Copy) { FileTree zip = zipTree(new File(project(':androusb-release').projectDir, 'androusb-release.aar')) from zip into 'src/main/jni/externalLibs' include ('**/**.so', '**/**.h') includeEmptyDirs = false } |
自前のnativeライブラリをリンクするタスク
これは特に何も目新しいことはしてません。今までに何回か記事にしてきたとおりAndroid.mkを使ってビルドするだけです。あっでも、Android.mk内で先ほどの作業ディレクトリ上にある共有ライブラリをリンクするように指定しといてね。
1 2 3 4 5 6 7 8 9 10 11 |
task ndkB(type: Exec, description: 'Compile JNI source via NDK') { println('executing ndkBuild') def ndkBuildingDir = project.plugins.findPlugin('com.android.library').sdkHandler.getNdkFolder().absolutePath def ndkBuildPath = ndkBuildingDir if (Os.isFamily(Os.FAMILY_WINDOWS)) { ndkBuildPath = ndkBuildingDir + '/ndk-build.cmd' } else { ndkBuildPath = ndkBuildingDir + '/ndk-build' } commandLine ndkBuildPath, '-j4', '-C', file('src/main').absolutePath } |
モジュールのビルド用にnativeライブラリをコピーするタスク
んでもって最後は出来上がったnativeライブラリをモジュールのビルド用にコピーするタスクです。今回は下記のコードに含まれる7つの共有ライブラリは元々androusb.aarに含まれていたものなのでexcludeしてコピーします(要は7つの共有ライブラリを除外してそれ以外を全部コピーするってことね)。
1 2 3 4 5 6 7 8 9 10 11 12 |
task ndkC(type: Copy) { // copy native libraries but excludes libraries came from androusb.aar from 'src/main/libs' into 'src/main/jniLibs' exclude '**/libgnustl_shared.so' exclude '**/libjpeg-turbo1500.so' exclude '**/libpng16.so' exclude '**/libcommon.so' exclude '**/libandrousb.so' exclude '**/libLV.so' exclude '**/libmediaencoder.so' } |
これだけだと単にタスクを定義しただけなので依存関係とモジュールに生成したnativeの共有ライブラリを含めてもらうように指示しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
apply plugin: 'com.android.library' import org.apache.tools.ant.taskdefs.condition.Os android { ... sourceSets { main { jniLibs.srcDir 'src/main/jniLibs' jni.srcDirs = [] } } ... } tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn ndkA, ndkB, ndkC } |
本当はちゃんと依存関係の順番を指定するほうがいいんですが、指定しなければアルファベット順に実行されるのでndkA, ndkB, ndkCと安直な名前にしてしまいました^^;
あとは大したこと無いでしょう。
1 |
jni.srcDirs = [] |
の方はいつもと同じでAndroid StudioのNDKサポートを無効にするおまじないですが、今回は更に
1 |
jniLibs.srcDir 'src/main/jniLibs' |
ちゅうおまじないも追加しています。この時のディレクトリパスはndkCタスクのコピー先と同じであればjniLibs以外の任意のディレクトリ名を使用できます。
ndk-buildスクリプトとAndroid.mkを使ってnativeライブラリをビルドすると成果物はsrc/main/libs下に入ります。
特に指定しなければAndroid Studio(gradle)もここから共有ライブラリをコピーしてしまうのですがそれでは元aarから抽出したnativeライブラリもコピーされてしまい重複して怒られてしまいます。そこでndkCタスクで必要なもののみjniLibsへコピーし、さらにそれをgradleがモジュールのビルドに使うというわけです。
Android Studioの次期バージョンではもしかしたらNDKサポートが向上してこんなことをしなくて済むのかもしれないという噂もあるし、まぁこんな事するはめになる人は少ないやろうけど参考になればいいな。
お疲れ様でしたぁ(^_^)/~
コメント
[…] 終的には4つ目の選択肢になるかも… 前に書いた自前のnativeライブラリビルド時にaar内にある既存のnativeライブラリとリンクした〜い^^/の部分が今のAndroid StudioのNDKサポートでは対応出 […]
[…] 以前書いた自前のnativeライブラリビルド時にaar内にある既存のnativeライブラリとリンクした〜い^^/ ではgradle.buildスクリプトで特定の共有nativeライブラリにリンクしつつもaarには出力され […]