Eclipse下的Android JNI 開發
1 建立Android工程
首先建立一個名為AndroidJniTest的Android工程,包名預設為com.example.androidjnitest,src目錄下自動建立MainActivity.java。
2 設計JNI介面
建立新的檔案包com.example.jni,並在改包下新建一個TestJNI.java的類。
開啟TestJNI.java,我們將在這個檔案裡建立一個JNI介面類,該Java類提供一個加法運算的介面:
public class TestJNI { public native boolean init(); public native int add(int x , int y); public native void destory(); }
注意,這裡的函式宣告要加上 native 關鍵字。
3 編譯JNI
將TestJNI.java檔案複製到工程的bin目錄下,在終端中進入該工程的bin目錄,輸入javac TestJNI.java,這時會生成一個TestJNI.class檔案。
在bin資料夾下,如果沒有則建立目錄:/com/example/jni,並把TestJNI.class複製到/bin/com/example/jni目錄下。然後在終端裡進入工程的bin目錄,輸入javah -jni com.example.jni.TestJNI,此時會生成一個
com_example_jni_TestJNI.h檔案就是對應於上面定義的Java介面的C/C++標頭檔案。開啟這個檔案,可以看到系統已經為我們自動完成了介面函式的宣告:
這三個函式分別對應於JNI的三個介面函式,命名方式只是在前面加上了Java包名。
4 用C/C++實現JNI
有了JNI的C/C++標頭檔案,就可以在C層實現JNI介面了。首先在工程目錄下建立一個jni目錄,這個目錄就是專門用來放C/C++程式碼的。把com_
由於我想用C++來實現JNI,所以上面兩個檔案我只是用來作為動態連結庫的介面,具體的實現我希望放在一個類裡面來完成,因此我再新增兩個檔案:Add.h和Add.cpp。
下面我們就來實現CAdd類和JNI介面。首先實現CAdd類:
CAdd.h
#ifndef JNI_TEST_ADD
#define JNI_TEST_ADD
class CAdd{
public:
CAdd();
~CAdd();
int add(int x, int y);
};
#endif
CAdd.cpp
#include "Add.h"
CAdd::CAdd(){
}
CAdd::~CAdd(){
}
int CAdd::add(int x, int y){
return x+y;
}
然後我們來寫com_example_jni_TestJNI.cpp,實現JNI:
#include <stdlib.h>
#include <stdio.h>
#include "Add.h"
CAdd *pCAdd = NULL;
JNIEXPORT jboolean JNICALL Java_com_example_jni_TestJNI_init
(JNIEnv *env, jobject obj){
if(pCAdd==NULL){
pCAdd = new CAdd ;
}
return pCAdd!=NULL;
}
JNIEXPORT jint JNICALL Java_com_example_jni_TestJNI_add
(JNIEnv *env, jobject obj, jint x, jint y){
jint res = -1;
if(pCAdd!=NULL){
res = pCAdd->add(x,y);
}
return res;
}
JNIEXPORT void JNICALL Java_com_example_jni_TestJNI_destory
(JNIEnv * env, jobject obj){
if(pCAdd!=NULL){
delete pCAdd;
pCAdd=NULL;
}
}
到此我們的 C/C++ 部分就全部實現了。
5 建立mk檔案
JNI實現了之後就要把C/C++程式碼編譯成動態連結庫.so檔案,這樣Java程式才能呼叫JNI的介面。要編譯so檔案,需要寫Android.mk和Application.mk兩個檔案。我們先來寫Android.mk。
先在工程目錄的jni下建立一個Android.mk檔案:
然後開啟檔案在裡面輸入如下內容:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TestJNI
LOCAL_SRC_FILES := com_example_jni_TestJNI.cpp
LOCAL_SRC_FILES += Add.cpp
include $(BUILD_SHARED_LIBRARY)
其中 LOCAL_PATH 是 C/C++ 程式碼所在目錄,也就是我們的 jni 目錄。
LOCAL_MODULE是要編譯的庫的名稱。編譯器會自動在前面加上lib,在後面加上.so。
LOCAL_SRC_FILES是要編譯的C/C++檔案。
現在我們在工程的根目錄下建立一個Application.mk檔案,並輸入如下內容:
APP_PROJECT_PATH := ${call my-dir}
APP_MODULES := TestJNI
6 編譯動態連結庫
寫完了mk檔案就可以開始編譯C/C++程式碼了。
預設在Windows7下配置好了NDK開發環境,開啟cygwin,進入到工程目錄。
在終端裡進入工程的根目錄,輸入命令“$NDK/ndk-build”命令即可編譯
編譯成功後會在工程目錄的libs/armeabi目錄下生成一個libTestJNI.so檔案。
7 在Java中呼叫JNI
現在我們的Android應用可以呼叫JNI計算加法的程式碼,如下:
static {
System.load("TestJNI");
}
TextView tvX = null;
TextView tvY = null;
TextView tvSum = null;
Button btnAdd = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvX = (TextView)findViewById(R.id.et_x);
tvY = (TextView)findViewById(R.id.et_y);
tvSum = (TextView)findViewById(R.id.et_sum);
btnAdd = (Button)findViewById(R.id.btn_add);
btnAdd.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
int x = Integer.valueOf( tvX.getText().toString());
int y = Integer.valueOf( tvY.getText().toString());
int sum = 0;
TestJNI jni = new TestJNI();
boolean flag = jni.init();
if(flag){
sum = jni.add(x, y);
}
btnAdd.setText(String.valueOf(sum));
}
});
}
程式執行結果: