Android NDK 〜導入編〜

Android

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

本日は弊社創立記念日です。

せっかくなので過去記事を探してもあまり無い、NDKについて書いていきたいと思います。

諸注意

前提条件として、Android Studioにて通常の開発が可能な状態まで整っていること。とさせて頂きます。

また、Gradleのバージョンによって記述が少し変わります。正常に動作しない場合は、環境をご確認ください。

今回は以下の環境で実施します。(どこで使うかは後述)

gradle-wrapper.properties

distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip

build.gradle

classpath 'com.android.tools.build:gradle-experimental:0.2.0'

Java

JDK 7で実行してください。

導入

NDKの導入

NDK_install_01

画像では既にインストール済みですが、特に注意する点はありません。

サクッとNDKをインストールしましょう。

インストールが完了したら、パスを通しておきます。(インストール箇所は適宜読み替えてください)

export PATH=$PATH:/Users/ユーザ名/Library/Android/sdk/ndk-bundle/

ターミナルにて、ndk-buildのコマンドが利用できればOKです。


gradleのバージョンを変更

gradleのバージョンを変更する必要があります。

gradle-wrapper.propertiesの該当箇所を変更します。

distributionUrl=https\://services.gradle.org/distributions/gradle-2.5-all.zip

一度、gradle syncして正常にインストールされるか確認しましょう。

(Project Structure > Project > Gradle version からでも変更できます。)


build.gradleの書き換え

gradleのバージョンを変更したことにより、build.gradleの記述方法が変わります。

Experimental Plugin User Guide - Android Studio Project Site

ここが分かりづらい所だと思います。導入では最小限の記載で進めましょう。

apply plugin: 'com.android.model.application'

model {
    android {
        compileSdkVersion = 23
        buildToolsVersion = "23.0.3"

        defaultConfig.with {
            applicationId = "jp.maruyama.cm.ndksample"
            minSdkVersion.apiLevel = 16
            targetSdkVersion.apiLevel = 23
            versionCode = 1
            versionName = "1.0"
        }
    }

    android.sources {
        main {
            jni {
                source {
                    srcDirs = [file("src/main/jni")]
                }
            }
            jniLibs {
                source {
                    srcDirs = []
                    srcDir "src/main/libs"
                }
            }
        }
    }

    android.ndk {
        moduleName = "app"
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.4.0'
}

ここまで来たら、もう一度syncしましょう。

正常にビルドが完了すれば、下準備は終了です!


nativeメソッドの追加

Javaソースに、nativeメソッドを追加します。

package jp.maruyama.cm.ndksample.util;

public class SampleUtil {

    static {
        System.loadLibrary("hello");
    }

    public native String hello();
}

記述完了後、Javaのビルドを行ってください。hファイル作成時に利用します。


hファイルの作成

hファイルは今回必要ないのですが、メソッド名をコピペするために作成します。

まず、Android Studioでターミナルを開いてください。

何もしていなければ、プロジェクトルートで起動するはずです。

その後、以下のコマンドを実行します。(パッケージ名は適宜読み替え)

javah -classpath app/build/intermediates/classes/debug -d app/src/main/jni クラスのパッケージ名.SampleUtil

正常に動作すれば、main配下のjniディレクトリに以下のようなhファイルが作成されます。

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class jp_maruyama_cm_ndksample_util_SampleUtil */

#ifndef _Included_jp_maruyama_cm_ndksample_util_SampleUtil
#define _Included_jp_maruyama_cm_ndksample_util_SampleUtil
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     jp_maruyama_cm_ndksample_util_SampleUtil
 * Method:    hello
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_jp_maruyama_cm_ndksample_util_SampleUtil_hello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

作成できない場合は、javahコマンドを確認してください。

また、Javaをビルドしてクラスファイルを作成していないと怒られます。

hファイルの名前は、hello.hにしておきましょう。


cファイルの作成

ここでようやくC言語の出番です。(たまに書くと楽しい!)

hello.c という名前でsrc/main/jni に作成します。

#include <jni.h>

//
// Created by maruyama.kenya on 2016/07/04.
//
JNIEXPORT jstring JNICALL Java_jp_maruyama_cm_ndksample_util_SampleUtil_hello(JNIEnv *env, jobject thiz) {
    return (*env)->NewStringUTF(env, "Hello World JNI!!");
}

hファイルから定義はコピってきましょう。引数名の書き忘れに注意してください。

あとincludeも忘れないようにしましょう。

次にmkファイルの作成です。コンパイル指示書みたいなものです。

src/main/jni に Android.mk というファイルを作成し以下を記述します。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello
LOCAL_SRC_FILES := hello.c

include $(BUILD_SHARED_LIBRARY)

makefileの詳細については、追々考えるとして今は気にせずコピペしてください。

ここまで来たら、ビルドしてみましょう。

Android Studio > Terminal

:NDKsample maruyama.kenya$ cd app/src/main/jni/
:jni maruyama.kenya$ ndk-build

カレントをjniにし、ndk-buildしてください。

正常にコンパイルされれば、soファイルが生成されます。


javaから呼び出し

準備ができましたので、Javaから実行してみましょう!

package jp.maruyama.cm.ndksample;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import jp.maruyama.cm.ndksample.util.SampleUtil;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        SampleUtil util = new SampleUtil();
        TextView view = (TextView) findViewById(R.id.jni_text);

        if (view != null) {
            view.setText(util.hello());
        }
    }
}

ndk_install_2

正常に表示されました!お疲れ様でした。

よくあるエラー

Java実行時のエラーが発生した際には、以下を確認しましょう。

  • JDKのバージョンが8系ではないか

  • build.gradleの記述にミスがないか

  • ndk-buildに失敗していないか

最後に

以上で、導入を終わります。

お気づきの点があれば、コメントに記載をお願い致します。