Android で スタティックライブラリを利用する

Android の開発時に、C++などで作られた 静的ライブラリ〜.a や、共有ライブラリ〜.so を利用したい時。

まず、ビルド設定方法は1つではない。CMakeLists.txt を使う方法、Android.mk を使う方法と、やり方が選べる。

また、 so ファイルは、アプリケーションコードからロードできるが、 a ファイルは直接ロードできず、so を経由して読み込むことになる。

選択する言語は、Java・Kotolin どちらでも全く問題ない。
ドキュメントだけでは理解できない部分があったのでまとめた。

公式のドキュメントはこちら。
プロジェクトへの C / C++ コードの追加  |  Android Developers

C、C++側の書き方はこちらを参照。
Java Native Interface仕様の目次

Android NDK環境のインストール

Android Studio を起動し、
Android SDK Manager > SDK Toolsタブ と進む。
CMake、LLDB、NDK を選択しインストールする。

プロジェクト作成

ここからは通常通り新規作成をするが、Choose your project で、Native C++ を選択する。
他のサイトでは、Include C++ support のチェックを入れるように書かれているが、その設定はもう無くなっており、必要ない。

プロジェクト内ファイルの説明

cpp フォルダの中に、CMakeLists.txt と、native-lib.cpp ファイルがある。
この2ファイルをいじるだけで、ビルドし、理解する分には十分です。

CMakeLists.txt の書き方

関数の引数は、スペースや改行で区切るイメージ。

共有ライブラリを作成時(C++をそのまま呼び出す場合)

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先
# ANDROID_ABIで、対応するプロセッサごとにフォルダを分ける
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリのソースファイル
# add_library( ライブラリ名 SHARED ソースファイル )
add_library( native-lib SHARED native-lib.cpp )

# 共有ライブラリ 出力先を指定
# set_target_properties( ライブラリ名 PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR} )
set_target_properties( native-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR} )

# ライブラリを変数に定義
# 必須ではないが、このような書き方が出来る
# find_library( 定義名 ライブラリ名... )
find_library( log-lib log )

# 依存ファイルのリンク
# native-lib.cpp の中で、${log-lib} を利用してプログラムをしている場合
# target_link_libraries( ライブラリ名 依存ファイル... )
target_link_libraries( native-lib ${log-lib} )
静的ライブラリを作成する時

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先設定
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリのソースファイル
# add_library( ライブラリ名 STATIC ソースファイル )
add_library( native-lib STATIC native-lib.cpp )

# 静的ライブラリ 出力先を指定
# set_target_properties( ライブラリ名 PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR} )
set_target_properties( native-lib PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR} )

build.gradle
リンクしない STATIC ライブラリはビルドされないため、常にビルドさせる設定

android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                # targets 'ライブラリ名'
                targets 'native-lib'
            }
        }
    }
}
共有ライブラリを利用する時

読み込めばC++の関数が呼び出せる。

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先設定
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリが外部参照である事を指定
add_library( native-lib SHARED IMPORTED )
# ライブラリファイルの位置を指定
set_target_properties( native-lib PROPERTIES IMPORTED_LOCATION ${OUTPUT_DIR}/libanylib.so )

*.java

class Hoge {
    static {
        System.loadLibrary("native-lib");
    }
}
静的ライブラリを利用する時

流れとしては、一度 静的ライブラリを元に、共有ライブラリを作成し、共有ライブラリを読み込むことになる。
なので、上記の方法を全て把握する必要がある。

CMakeLists.txt

# ライブラリのルート
set( LIB_ROOT ${CMAKE_CURRENT_SOURCE_DIR}/../.. )
# ライブラリの出力先
set( OUTPUT_DIR ${LIB_ROOT}/lib/${ANDROID_ABI} )

# ライブラリが外部参照である事を指定
add_library( anylib STATIC IMPORTED )
# ライブラリファイルの位置を指定
set_target_properties( anylib PROPERTIES IMPORTED_LOCATION ${OUTPUT_DIR}/libanylib.a )

# 共有ライブラリの生成
add_library( native-lib SHARED native-lib.cpp )
# 共有ライブラリ 出力先を指定
set_target_properties( native-lib PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR} )

# ライブラリのインクルードファイルの参照先
target_include_directories( native-lib PRIVATE ${LIB_ROOT}/include )

# ライブラリを変数に定義
find_library( log-lib log )

# 依存ファイルのリンク
# native-lib.cpp の中で、anylibと、${log-lib} を利用してプログラムをしている場合
target_link_libraries(
        native-lib
        anylib
        ${log-lib} )

*.java

class Hoge {
    static {
        System.loadLibrary("native-lib");
    }
}