JNI相關

Android JNI和NDK關係
一、什麼JNI
Java Native Interface (JNI)標準是Java平臺的一部分,它容許Java代碼和其餘語言寫的代碼進行交互。JNI 是本地編程接口,它使得在 Java 虛擬機 (VM) 內部運行的 Java 代碼可以與用其它編程語言(如 C、C++ 和彙編語言)編寫的應用程序和庫進行交互操做。
上面過程分爲2個部分:
第1、用C語言生成一個庫文件。
第2、在java中調用這個庫文件的函數。
二、NDK
NDK全稱:Native Development Kit。
NDK是一系列工具的集合。
* NDK提供了一系列的工具,幫助開發者快速開發C(或C++)的動態庫,並能自動將so和java應用一塊兒打包成apk。這些工具對開發者的幫助是巨大的。
* NDK集成了交叉編譯器,並提供了相應的mk文件隔離CPU、平臺、ABI等差別,開發人員只須要簡單修改mk文件(指出「哪些文件須要編譯」、「編譯特性要求」等),就能夠建立出so。
* NDK能夠自動地將so和Java應用一塊兒打包,極大地減輕了開發人員的打包工做。
我的理解,NDK就是可以方便快捷開發.so文件的工具。
JNI的過程比較複雜,生成.so須要大量操做,而NDK就是簡化了這個過程。css

registerNativeMethodshtml

傳統java Jni方式:1.編寫帶有native方法的Java類;—>2.使用javah命令生成.h頭文件;—>3.編寫代碼實現頭文件中的方法,這樣的「官方」 流程,但也許有人沒法忍受那「醜陋」的方法名稱,
通用方式:RegisterNatives方法能幫助你把c/c++中的方法隱射到Java中的native方法,而無需遵循特定的方法命名格式。應用層級的Java類別透過VM而呼叫到本地函數。通常是仰賴VM去尋找*.so裏的本地函數。若是須要連續呼叫不少次,每次都須要尋找一遍,會多花許多時間。此時,組件開發者能夠自行將本地函數向VM進行登記,VM調registerNativeMethods()函數的用途有二:  (1)更有效率去找到函數。  (2)可在執行期間進行抽換。因爲gMethods[]是一個<名稱,函數指針>對照表,在程序執行時,可屢次呼叫registerNativeMethods()函數來更換本地函數之指針,而達到彈性抽換本地函數之目的。java

cpp文件中 android

static JNINativeMethod methods[] = {  
    {"native_setText","([BJI)I",(void*)native_setText },  
    {"native_clearText","(I)I",(void*)native_clearText },  
    {"native_create","(I)I",(void*)native_create },  
    {"native_start","()I",(void*)native_start },  
    {"native_next","()I",(void*)native_next },  
    {"native_flush","()I",(void*)native_flush },  
    {"native_stop","()I",(void*)native_stop },  
    {"native_pause","()I",(void*)native_pause },  
    {"native_resume","()I",(void*)native_resume },  
    {"native_get_current_position","()J",(void*)native_get_current_position },  
    {"native_setSpeed","(I)I",(void*)native_setSpeed },  
    {"native_getSynSpeed","()I",(void*)native_getSynSpeed },  
    {"native_finalize","()I",(void*)native_finalize },  
    {"native_delete","()I",(void*)native_delete },  
};  

//Class path name for Register 
static const char *classPathName = "android/tts/TTS";  

/* * Register several native methods for one class. */  
static int registerNativeMethods(JNIEnv* env, const char* className,  
                                 JNINativeMethod* gMethods, int numMethods)  
/* * Register native methods for all classes we know about. * * returns JNI_TRUE on success. */  
static int registerNatives(JNIEnv* env)  
{  
    if (!registerNativeMethods(env, classPathName,  
                               methods, sizeof(methods) / sizeof(methods[0]))) {  
        return JNI_FALSE;  
    }  


    return JNI_TRUE;  
}

java文件中 c++

static native int native_create(int streamType);  
    static native int native_setSpeed(int speed);  
    static native int native_setText(byte str[],long length,int islast);  
    static native int native_clearText(int appID);  
    static native int native_start();  
    static native int native_next();  
    static native int native_flush();  
    static native int native_pause();  
    static native int native_resume();  
    static native int native_stop();   
    static native int native_finalize();  
    static native int native_delete();  
    static native int native_getSynSpeed();  
    static native boolean native_isPlaying();  
    static native long native_get_current_position();

JNI組件的入口函數——JNI_OnLoad()、JNI_OnUnload()
JNI組件被成功加載和卸載時,會進行函數回調,當VM執行到System.loadLibrary(xxx)函數時,首先會去執行JNI組件中的JNI_OnLoad()函數,而當VM釋放該組件時會呼叫JNI_OnUnload()函數。先看示例代碼:編程

typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid; 數組

/* This function will be call when the library first be loaded */
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
UnionJNIEnvToVoid uenv;
JNIEnv* env = NULL;
LOGI(「JNI_OnLoad!」); markdown

if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {  
    LOGE("ERROR: GetEnv failed");  
    return -1;  
}  

env = uenv.env;;  

if (registerNatives(env) != JNI_TRUE) {  
    LOGE("ERROR: registerNatives failed");  
    return -1;  
}  

 return JNI_VERSION_1_4;

} oracle

JNI_OnLoad()有兩個重要的做用:
指定JNI版本:告訴VM該組件使用那一個JNI版本(若未提供JNI_OnLoad()函數,VM會默認該使用最老的JNI 1.1版),若是要使用新版本的JNI,例如JNI 1.4版,則必須由JNI_OnLoad()函數返回常量JNI_VERSION_1_4(該常量定義在jni.h中) 來告知VM。
初始化設定,當VM執行到System.loadLibrary()函數時,會當即先呼叫JNI_OnLoad()方法,所以在該方法中進行各類資源的初始化操做最爲恰當。
JNI_OnUnload()的做用與JNI_OnLoad()對應,當VM釋放JNI組件時會呼叫它,所以在該方法中進行善後清理,資源釋放的動做最爲合適。app

JNI中的日誌輸出
你必定很是熟悉在Java代碼中使用Log.x(TAG,「message」)系列方法,在c/c++代碼中也同樣,不過首先你要include相關頭文件。遺憾的是你使用不一樣的編譯環境( 請參考上文中兩種編譯環境的介紹) ,對應的頭文件略有不一樣。。
若是是在完整源碼編譯環境下,只要include

#define LOG_TAG "HelloJni" 
#define LOG_NDEBUG 0 
#define LOG_NIDEBUG 0 
#define LOG_NDDEBUG 0 


#include <string.h> 
#include <jni.h> 
#include <utils/Log.h> 
jstring Java_com_inc_android_ime_HelloJni_stringFromJNI(JNIEnv* env,jobject thiz){  
    LOGI("Call stringFromJNI!\n");  
    return (*env)->NewStringUTF(env, "Hello from JNI (中文)!");  
}

JNI 的對應數據類型針對java和c++
c/c++方法和Java方法之間映射關係的關鍵是 JNINativeMethod 結構,該結構定義在jni.h中,具體定義以下:
[cpp] view plain copy

typedef struct {   
  const char* name;//java方法名稱 
  const char* signature; //java方法簽名 
  void*       fnPtr;//c/c++的函數指針 
} JNINativeMethod;

參照上文示例中初始化該結構的代碼:
[cpp] view plain copy
//定義方法隱射關係

static JNINativeMethod methods[] = {  
  {"sayHello", "(Ljava/lang/String;)Ljava/lang/String;", (void*)sayHello},  
};

其中比較難以理解的是第二個參數——signature字段的取值,實際上這些字符與函數的參數類型/返回類型一一對應,其中」()」 中的字符表示參數,後面的則表明返回值。例如」()V」 就表示void func(),」(II)V」 表示 void func(int, int),具體的每個字符的對應關係以下:
字符 Java類型 C/C++類型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short

數組則以」[「開始,用兩個字符表示:
字符 java類型 c/c++類型
[Z jbooleanArray boolean[]
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]

上面的都是基本類型,若是參數是Java類,則以」L」開頭,以」;」結尾,中間是用」/」隔開包及類名,而其對應的C函數的參數則爲jobject,一個例外是String類,它對應C類型jstring,例如:Ljava/lang /String; 、Ljava/net/Socket; 等,若是JAVA函數位於一個嵌入類(也被稱爲內部類),則用 "Landroid/os/FileUtils FileStatus;」。

In C, all other JNI reference types are defined to be the same as jobject. For example:
typedef jobject jclass; 
In C++, JNI introduces a set of dummy classes to enforce the subtyping relationship. For example:
class _jobject {}; 
class _jclass : public _jobject {}; 
... 
typedef _jobject *jobject; 
typedef _jclass *jclass; 
Field and Method IDs
Method and field IDs are regular C pointer types:
struct _jfieldID;              /* opaque structure */ 
typedef struct _jfieldID *jfieldID;   /* field IDs */ 

struct _jmethodID;              /* opaque structure */ 
typedef struct _jmethodID *jmethodID; /* method IDs */ 
The Value Type
The jvalue union type is used as the element type in argument arrays. It is declared as follows:
typedef union jvalue { 
    jboolean z; 
    jbyte    b; 
    jchar    c; 
    jshort   s; 
    jint     i; 
    jlong    j; 
    jfloat   f; 
    jdouble  d; 
    jobject  l; 
} jvalue; 
Type Signatures
The JNI uses the Java VM’s representation of type signatures. Table 3-2 shows these type signatures.
Table 3-2 Java VM Type Signatures
Type Signature
Java Type
Z
boolean
B
byte
C
char
S
short
I
int
J
long
F
float
D
double
L fully-qualified-class ;
fully-qualified-class
[ type
type[]
( arg-types ) ret-type
method type
For example, the Java method:
long f (int n, String s, int[] arr); 
has the following type signature:
(ILjava/lang/String;[I)J

參考的資料:http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/types.html#wp568
http://www.top-e.org/jiaoshi/html/?168.html
http://neillife.blogspot.com/2009/01/how-to-add-new-module-to-android.html
http://blog.csdn.net/zhenyongyuan123/article/details/5862054
http://download.oracle.com/javase/1.5.0/docs/guide/jni/spec/jniTOC.html
http://cnetwei.iteye.com/blog/825306

相關文章
相關標籤/搜索