介紹

主要參考這篇:Android studio 1.5.1 NDK JNI環境安裝與執行原理
不過Android 2.1.2上步驟稍有簡化,還是可以達到同樣效果。

  1. 載NDK
  2. 設定external tool (僅javah)
  3. 在java中增加native code,並利用javah產生JNI header
  4. 實作JNI
  5. 設定gradle (僅ndk區塊)

載NDK

開啟SDK Manager,選擇SDK Tools,將NDK之選項給打勾。
SDK Manager

設定external tool

與連結相同,不過只要設定javah即可,其他不需要。

javah設定

快速複製區 來源

$JDKPath$/bin/javah

-v -jni -d $ModuleFileDir$/src/main/jni $FileClass$

$SourcepathEntry$

Windows和Linux在第1行稍有分別,只有在Windows系統中,執行檔的副檔名才是.exe。Mac和Linux皆否。 個人是用linux系統,所以第1行javah後面並不接.exe,若是Windows系統則第1行變為。

$JDKPath$/bin/javah.exe

JNI header

在想要使用JNI的class加入如下的程式碼。

    static {
            System.loadLibrary("myJNI");
    }
    public native String getMycstring();
    public native void testLog();

static區塊中的myJNI會變成將來再build gradle當中的moduleName
method加上native關鍵詞後,程式就會知道這是需要依靠JNI實作之程式碼。

再來在該java檔上按右鍵,使用剛剛建置好的external library javah,自動的產生JNI header。
使用javah

此時在java目錄下,應該會多出一個jni的資料夾,裡頭放置著 OOXX.h
打開.h檔,建置出來的code應該會如下形式

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

#ifndef _Included_com_example_isa_myapplication_MainActivity
#define _Included_com_example_isa_myapplication_MainActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_isa_myapplication_MainActivity
 * Method:    getMycstring
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_isa_myapplication_MainActivity_getMycstring
  (JNIEnv *, jobject);

/*
 * Class:     com_example_isa_myapplication_MainActivity
 * Method:    testLog
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_isa_myapplication_MainActivity_testLog
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

實作JNI

步驟如下

  1. 在jni資料夾中,新增一個myJNI.cpp
  2. include剛剛自動產生的header
  3. 實作剛剛只宣告未實作的程式碼
  4. (細節注意)

新增myJNI.cpp

在jni資料夾中,新增一個myJNI.cpp

include剛剛自動產生的header

#include "com_example_isa_myapplication_MainActivity.h"

實作剛剛只宣告未實作的程式碼

由於Log功能要使用到android/log.h library,所以也要include進來。

#include "com_example_isa_myapplication_MainActivity.h"
#include <android/log.h>

JNIEXPORT jstring JNICALL Java_com_example_isa_myapplication_MainActivity_getMycstring
  (JNIEnv *env, jobject jobj){
        return (*env).NewStringUTF("MY !!  NDKString!!");
  }
  
JNIEXPORT void JNICALL Java_com_example_isa_myapplication_MainActivity_testLog
  (JNIEnv * env, jobject jobj){
    __android_log_print(ANDROID_LOG_INFO, "JNI", "JNI Test");
  }

細節注意

當然僅僅如此還不夠,觀看.h檔可以發現有extern “C”包圍著宣告的函式,所以也要一併複製。

#include "com_example_isa_myapplication_MainActivity.h"
#include <android/log.h>


#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL Java_com_example_isa_myapplication_MainActivity_getMycstring
  (JNIEnv *env, jobject jobj){
        return (*env).NewStringUTF("MY !!  NDKString!!");
  }
  
  JNIEXPORT void JNICALL Java_com_example_isa_myapplication_MainActivity_testLog
  (JNIEnv * env, jobject jobj){
    __android_log_print(ANDROID_LOG_INFO, "JNI", "JNI Test");
  }
  
#ifdef __cplusplus
}
#endif

切記不要連 #ifndef _Included_com_example_isa_myapplication_MainActivity 的相關句子也複製進來。
在.h檔已經定義過,若在.cpp重複定義,則中間的所有內容都會被忽略掉。

設定gradle

在defaultConfig這個區塊內加入

ndk{
  moduleName "myJNI"
  ldLibs "log"
}

moduleName為你剛剛在System.loadLibrary裡面設定的名字。
ldLibs “log”可以讓cpp檔include log功能的時候不會出錯。

整體看起來是這樣

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.example.isa.myapplication"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        ndk{
            moduleName "myJNI"
            ldLibs "log"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

最後編譯就完成了。

參考資料

Android studio 1.5.1 NDK JNI環境安裝與執行原理