見出し画像

Android Studioでndkを扱う(1)

はじめに

ごく一般的な方は、Androidの開発においてndkを利用ことは無いと思います。しかしながら、Android1.xの頃はAPIで出来ることが少なく、LinuxソースコードをAndroid端末に移植しActivityで利用することが流行っており、それこそOpenCVを自分で改修してAndroidで動作させたといった話題が、twitterで流れ、開発者は挙って我先に新しいものを作っていたものです。

私も、Android 1.5から開発を始めEclipse+CDTでAndroid ndkの開発を行っていました。未だにその当時の備忘録は私のBlogに残ってはいるものの、古すぎて現在では利用できません。
技術的エッセンスは分かる人には分かるのですが、そのような方はそもそも自力でなんとかできるでしょう。

昔話をすると長くなるので、Android Studio(今回はArctic Fox版)で簡単?にndkの開発環境とAndroidの組み込みまでができるガイドを作ることにします。

ndkって何?

Android ndkは以下のリンクからでも読んで頂くとして、以下のことは知っている前提で始めます。

  • ndkの概要程度は知っている

  • C/C++言語は知っている(勉強中でも構いませんが、Cって何?は当記事の対象外です)

  • Android Studioは知っていて、Java/Kotlinでサンプルアプリ程度は動かせる程度は使える

  • JNIについては知っている、あるいは勉強する

ndkをAndroid Studioでサンプルアプリを作る

File→New→New Project→Native C++でHellocmakeを生成しビルドすれば動作します。これで解説終わり・・・

いえいえ、Android ndkでソースコードを一から書き始めるって人は、現在では特別な例外を除き皆無でしょう。
そのため、HellocmakeはSDK Managerからndkをインストール出来ればだれでも動かすことが出来ますが、実用的ではありません。
ndkを利用したいと思う方は、Linuxで動作しているC/C++のオープンソースを利用し、Java/Kotlinで作っているアプリから呼び出して利用したいと考える方が大半だと思うからです。
※Android1.xの頃でも、ApacheソースをndkでビルドしてWiFiで他のAndroid端末へhtml配信できたとかの話題があったもので、10数年前も同じです。

説明よりも、実際のサンプルプロジェクトを無料で見せろって方は以下のリンクから見て下さい。w
とはいえ、自分で作る際にはこのブログから始まるシリーズを見ることをおすすめします。

Android ライブラリの作成目的

Androidでライブラリを作る場合、File→New→New Module→Android Libraryで作ることになります。

一般的なndkライブラリを開発する際のシチュエーションはどのようなタイミングなのでしょうか?

私の経験上、「Java/Kotlinで実行しているが性能が出ないので、一部をndkで高速化したい」「機能拡張時、Open Sourceを持ってくることで解決したい」だったりします。
2つ目なのですが、Open Sourceは各Licenceが存在します。これを無視して発見されたらひどい目に合うのでライセンス条項はちゃんと守りましょう。
特にGPL(GNU General Public Licence)の場合、公開しなければならない可能性まで生じます。それでも使いたい場合、ライブラリとして分離独立させ、インタフェースも独自で実装することで本体自体のソースコード公開は避けるといった商用利用で実装する場合の技術になります。

私の記事は、基本的に商用利用するけれど分離独立させることで公開しても問題ないようにする人が対象となります。
オープンソース開発の方には関係の無い話ではあるのですが、世の中大半は商用系開発でしょう。

はじめてのndkライブラリの作り方

0.ndkのパスを通しておく

Android StudioをインストールしndkもSDK Managerからダウンロードしておきます。
その後、ndkライブラリ等のパスをターミナルから使えるようにしておきます。

export ANDROID_NDK_ROOT=$HOME/Android/Sdk/ndk/23.1.7779620
export ANDROID_SDK_ROOT=$HOME/Android/Sdk
export PATH=$ANDROID_SDK_ROOT/tools:$ANDROID_NDK_ROOT:$PATH
export PATH=$ANDROID_SDK_ROOT/platform-tools:$PATH

上記は、私のUbuntの.bashrcの最下部に記述している内容です。
これは一例なので、自分の環境に合わせて設定してください。

<補足>
開発環境のOSとしてはUbuntu OSを使っています。理由としては、AndroidのベースOSはLinuxであるためndk開発する前にC/CPPを作って検証した後に、ndkに移植する方法がデバッグが非常に楽になります。
そのため、ndkでのデバッグはc/cppのデバッグでは無くjava wrapperのデバッグとすることで、バグの混入範囲を狭めることができますので。

1.空プロジェクトの生成

単独のライブラリプロジェクトを生成することが出来ないためです。
File→New→New Projectを選択しNew Projectダイアログを表示します。

New Project

Activity(スマホ上の表示画面)は特に不要ですので、No Activityでも構いません。ライブラリ作成時に不要なので消します。
ここではNo Activityを選択した状態で話を進めます。
NameやPackage name、Minimum SDKは自分の必要な条件を設定しますが、ここでは、下記サンプル画面で話を進めます。

New Project

Finishを押下し暫く経過すると、プロジェクトの雛形が表示されます。
appの中身は全て削除します(必要ならそのままにしても良いです)。

2.ライブラリモジュールの作成

File→New→New Moduleを選択します。

Create New Module

ここでは、Android Libraryを選択しています。これは、NDKのC/C++ソース単独では無く、これをJavaラッパーで包んだライブラリを提供することとしています。
但し、Android Native Libraryを選択した場合は、恐らくcmakeでのライブラリになるかと思います。今回の例では、ndk-buildで手動ビルドを行うパターンの方法となります。

Java WrapperでJNIを隠蔽するため、ライブラリ名をここではjavawrapperとして設定しました。
Finishボタンを押下するとライブラリモジュールが生成されます。

3.不要なモジュールの削除

空プロジェクトは特に必要ない場合、モジュールを削除します。
必要ならこのステップは不要です。
File→Project Structureを選択して表示します。

Project Structure

Modules欄のappを選択し、マイナスボタンを押します。

Remove Module

モジュール削除の問い合わせが来ますので、Yesを押すことで削除できます。
削除されるものの、ソースやディレクトリなどの実体は残っています。
実体まで綺麗に消したい場合、左ペインをProjectに切り替えてappを選択し右クリックメニューからDeleteを選択することで削除できます。

Project
Delete

まだRun/Debug Configurationsが残っています。ツールアイコンのappを押して、Edit Configurations…を選択します。

Run/Debug Configurations

appを選択し左上部のマイナスボタンを押すことで削除することができます。

4.Native開発環境の準備

nativeライブラリをcmakeで作成する場合には、cmakeファイルを作成することになりますが、今回はndk-buildを使った手動ビルドで実行する形式です。
どちらかと言うと旧来のndkと同じやり方なのですが、個人的にはこちらの方が好きです。
jniの説明は探しても本家ndk情報以外にはほぼ壊滅な状態でした。そのため、ここではjni手動ビルドでの説明です。
まず、ここまでのディレクトリ状態を見てみます。

appを削除した状態のndklibプロジェクト直下

javawrapper内に移動し、jniディレクトリを新規作成します。

新規ディレクトリ作成ダイアログ
jniディレクトリ作成済

5.Nativeファイル類一式を作成する

jniディレクトリ内に作成しなければならないものは以下の通り

  • c/cppソース(c/cppが無いとそもそも・・・)

  • hソース(ヘッダーファイルは必要な場合のみ)

  • Application.mk(ライブラリ生成の指示ファイル)

  • Android.mk(コンパイルソース、ライブラリ名など)

このcppソースは、hellocmakeを生成した際のサンプルをベースとしています。ファイル名などは別ですが中身自体は似ています。

サンプルソース画面
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_javawrapper_nativecall_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    char hello[20] = "Hello from C++";
    return env->NewStringUTF(hello);
}

jniは呼び出しを行うモジュールのパッケージ名+クラス名までが記載された関数名になることでセキュリティに配慮されています。
そのため、色んな場所から呼び出しされる汎用的なnativeライブラリは作成できないため、Javaラッパーを噛ませることで汎用性を持たせるといった意味もあります。

6.Nativeライブラリのビルド

javaラッパーの準備が出来ていなくても前述までの準備ができていれば、ndkのビルドすることは可能です。
Android StudioのTerminal→cd javawrapperとしてディレクトリがjni
があるところに移動します。

ndklib/javawrapper$ ndk-build -B
ndk-build -Bのログ

上記の0.から6.までが済んでいる場合、ndk-buildコマンドが利用できます。コマンド実行後、コンパイルエラーが無ければlibsディレクトリが作成されて各cpu毎にバイナリlibhello_jni.soが作成されます。

上記の画面キャプチャではエラーがあったので、エラーが表示されていますが何のエラーだったかは既に覚えておりません。w

7.呼び出し用Javaモジュールの作成とビルド

javaソース(Kotlinでも可)は好きに作って頂ければ良いのですが、ここではサンプルとしてnativecall.javaクラスを作成しました。

クラス作成直後
package com.example.javawrapper;

public class nativecall {

    // Used to load the 'hello_cmake' library on application startup.
    static {
        System.loadLibrary("hello_jni");    // LOCAL_MODULE name in Android.mk
    }

    /**
     * A native method that is implemented by the native library in jni,
     * which is packaged with this library.
     */
    native String stringFromJNI();

    public String getdata() {
        return stringFromJNI();
    }
}

内容は上記の通りです。
loadlibrary命令はlibhello_jni.soの先頭からlibと末尾の.soを取り除いた内容を設定することになっています。

build.gradleの内容などが適切であれば、javawrapperはAndroid StudioのメニューBuild→Rebuild Projectでビルドできます。

ビルド済画面

成功すると、左側のProjectペインのjavawrapper→ build→ outputs→ aar→ javawrapper-debug.aarとなってaarファイルが生成されています。
これはデバッグビルドになります。
Android StudioでのReleaseビルドはapkの場合はGenerate Signed Bundle or APKとなります。
debugとreleaseの切り替えについてですが、メニューBuild→ Select Build Variantで切り替えます。gradlewコマンドで強制的にリリースビルドを行う方法もあります。

ndkライブラリ部分のみ説明は終わりました。
次回は、ndkライブラリのテスト用アプリを別モジュールとして作成し、結合しaarやlibsの話も記載します。

写真は2016年7月16日の殺生石です。現在は割れていますが、当時はまだ割れていませんでした。


この記事が気に入ったらサポートをしてみませんか?