Android JNI 環境建置
Contents
介紹
主要參考這篇:Android studio 1.5.1 NDK JNI環境安裝與執行原理
不過Android 2.1.2上步驟稍有簡化,還是可以達到同樣效果。
- 載NDK
- 設定external tool (僅javah)
- 在java中增加native code,並利用javah產生JNI header
- 實作JNI
- 設定gradle (僅ndk區塊)
載NDK
開啟SDK Manager,選擇SDK Tools,將NDK之選項給打勾。
設定external tool
與連結相同,不過只要設定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。
此時在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
步驟如下
- 在jni資料夾中,新增一個myJNI.cpp
- include剛剛自動產生的header
- 實作剛剛只宣告未實作的程式碼
- (細節注意)
新增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'
}
}
}
最後編譯就完成了。