使用Eclipse和NDK開發Android JNI工程

基本流程:
1.新建Android工程
2.Java文件中創建調用接口聲明代碼
3.javah生成C/C++的.h文件
4.編寫C/C++實現代碼
5.編寫Android.mk腳本
6.編寫Application.mk腳本
7.使用ndk編譯工程生成.so動態庫
8.編寫加載庫的代碼載入.so文件
9.調用動態庫的接口方法

詳細流程:
1.新建工程,並對ndk進行配置

工程AndroidJniTest

右鍵選擇工程屬性,按照圖中1,2,3步驟

右鍵選擇工程屬性

雙擊工程Builder,選擇ndk-build.cmd所在路徑以及工程的工作目錄

這裏寫圖片描述

按照如圖所示配置ndk執行

這裏寫圖片描述

這裏寫圖片描述

點擊Specify Resources按鈕

這裏寫圖片描述

2.創建com.example.jni包,並創建TestJNI.java文件,聲明接口,內容如下:

package com.example.jni;

public class TestJNI {
     public native boolean init();
     public native int add (int x, int y);
     public native void destroy();
}

3.使用命令:javah -classpath bin/classes -d jni com.example.jni.TestJNI
生成jni調用的.h文件,叫com_example_jni_TestJNI.h
參數解釋:
-classpath 使用類的位置
-d jni需要生成jni的類,包括包名+類名
這個.h文件裏面的內容如下:

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

#ifndef _Included_com_example_jni_TestJNI
#define _Included_com_example_jni_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jni_TestJNI
 * Method:    init
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_com_example_jni_TestJNI_init
  (JNIEnv *, jobject);

/*
 * Class:     com_example_jni_TestJNI
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_jni_TestJNI_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     com_example_jni_TestJNI
 * Method:    destroy
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jni_TestJNI_destroy
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

4.編寫實現這些接口的C++代碼
首先是Add.h,代碼如下:

#ifndef JNI_TEST_ADD
#define JNI_TEST_ADD
class CAdd {
public:
     CAdd();
     ~CAdd();
     int add(int x, int y);
};
#endif

然後是Add.cpp,代碼如下:

#include "Add.h"
CAdd::CAdd (){

}
CAdd::~CAdd (){

}
int CAdd::add( int x, int y){
     return x+y;
}

最後是com_example_jni_TestJNI.cpp,代碼如下:
PS:最好先把.h的代碼先複製下來,然後再進行改動,防止缺少什麼。
比如缺少jni.h和extern”C”都會有相應的問題(JNI_EXPORT/Native method not found等),導致不能編譯成功

#include<stdio.h>
#include <stdlib.h>
#include <jni.h>
#include "Add.h"

#ifndef _Included_com_example_jni_TestJNI
#define _Included_com_example_jni_TestJNI
#ifdef __cplusplus
extern "C" {
#endif

CAdd *pcAdd = NULL;

/*
 * Class:     com_example_jni_TestJNI
 * Method:    init
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_com_example_jni_TestJNI_init
  (JNIEnv *env, jobject object){
     if(pcAdd==NULL){
           pcAdd = new CAdd;
     }
     return pcAdd!=NULL;
}

/*
 * Class:     com_example_jni_TestJNI
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_jni_TestJNI_add
  (JNIEnv *env, jobject object, jint x, jint y){
     jint res = -1;
     if(pcAdd != NULL){
           res = pcAdd->add(x,y);
     }
     return res;
}

/*
 * Class:     com_example_jni_TestJNI
 * Method:    destroy
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jni_TestJNI_destroy
  (JNIEnv *env, jobject object){
     if(pcAdd != NULL){
            delete pcAdd;
           pcAdd = NULL;
     }
}

#ifdef __cplusplus
}
#endif
#endif

5.編寫Android.mk和Application.mk文件
(Android.mk和.cpp可以通過右鍵項目->Android Tools->Add Native Support生成)

Android.mk內容:

LOCAL_PATH := $(call my-dir) #Android.mk的目錄路徑

include $(CLEAR_VARS) #清理LOCAL_xxx避免相互影響

LOCAL_MODULE    : = TestJNI #要生成的動態庫名稱

### Add all source file names to be included in lib separated by a whitespace
LOCAL_SRC_FILES := com_example_jni_TestJNI.cpp\
Add.cpp #要編譯的jni和底層源碼

include $(BUILD_SHARED_LIBRARY) #生成動態庫

Application.mk內容:

APP_STL := gnustl_static #使用STL庫
APP_CPPFLAGS += -fpermissive #忽略一些語法錯誤,兼容老語法使其通過

makefile文件參數的解釋:
Android NDK學習 <二> Android.mk的製作
Android NDK開發指南(一) Application.mk文件

Android.mk告訴NDK Build系統關於Source的信息。Android.mk將是GNU Makefile的一部分,且將被Build System解析一次或多次。所以,儘量少的在Android.mk中聲明變量,也不要假定任何東西不會在解析過程中定義。

LOCAL_PATH := $(call my-dir)

每個Android.mk文件必須以定義LOCAL_PATH爲開始。它用於在開發tree中查找源文件。
宏my-dir 則由Build System提供。返回包含Android.mk的目錄路徑。

include $(CLEAR_VARS)

CLEAR_VARS 變量由Build System提供。並指向一個指定的GNU Makefile,由它負責清理很多LOCAL_xxx.
例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH. 這個清理動作是必須的,因爲所有的編譯控制文件由同一個GNU Make解析和執行,其變量是全局的。所以清理後才能避免相互影響。

LOCAL_MODULE := TestJNI

LOCAL_MODULE模塊必須定義,以表示Android.mk中的每一個模塊。名字必須唯一且不包含空格。Build System會自動添加適當的前綴和後綴。例如,foo,要產生動態庫,則生成libfoo.so.
但請注意:如果模塊名被定爲:libfoo.則生成libfoo.so. 不再加前綴。

LOCAL_SRC_FILES := 
com_example_jni_TestJNI.cpp\ #\和回車隔開
Add.cpp

LOCAL_SRC_FILES變量必須包含將要打包如模塊的C/C++ 源碼。不必列出頭文件,build System 會自動幫我們找出依賴文件。
缺省的C++源碼的擴展名爲.cpp. 也可以修改,通過LOCAL_CPP_EXTENSION。

include $(BUILD_SHARED_LIBRARY)

BUILD_SHARED_LIBRARY:是Build System提供的一個變量,指向一個GNU Makefile Script。它負責收集自從上次調用 include $(CLEAR_VARS) 後的所有LOCAL_XXX信息。並決定編譯爲什麼。

APP_STL :默認,NDK構建系統提供由Android系統給出的最小C++運行時庫(/system/lib/libstdc++.so)的C++頭文件。
然而,NDK帶有另一個C++實現,你可以在你自己的應用程序中使用或鏈接它。
定義APP_STL以選擇它們其中的一個:
APP_STL := stlport_static –> static STLport library
APP_STL := stlport_shared –> shared STLport library
APP_STL := system –> default C++ runtime library

APP_CFLAGS : 一個C編譯器開關集合,在編譯任意模塊的任意C或C++源代碼時傳遞。
它可以用於改變一個給定的應用程序需要依賴的模塊的構建,而不是修改它自身的Android.mk文件

6.編譯工程,生成動態鏈接庫.so文件
一般改完會自動編譯,如果沒有反應可以在cmd中跳轉到jni目錄,使用ndk-build對工程進行編譯,這一方法在Android Studio中同樣適用。

7.調用.so文件,運用在Activity中
其MainActivity.java內容如下:

package com.example.androidjnitest;

import com.example.jni.TestJNI;

import android.os.Bundle;
import android.app.Activity;
import android.widget.TextView;

public class MainActivity extends Activity {

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

     TextView tv_test;

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

           InitView();
           TestJNI jni = new TestJNI();
           jni.init();
            tv_test.setText( "1+1="+jni.add(1,1));
     }

     void InitView(){
            tv_test = (TextView) findViewById(R.id. tv_test);
     }
}

將其發佈到手機或者Android虛擬機上即可看見結果。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章