日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

java类sample是公共的_应在名samle.java的文件_Andoid NDK编程 1 - 注册native函数

發布時間:2023/12/4 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java类sample是公共的_应在名samle.java的文件_Andoid NDK编程 1 - 注册native函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

打算對Android的NDK的開發做一總結,首先是JNI部分,接下來是NDK的內容。今天首先介紹一下JNI的第一部分:注冊native函數。

當java代碼中執行native的代碼時候,首先是通過一定的方法來找到這些native方法。而注冊native函數的具體方法的不同,會導致系統在運行時采用不同的方式來尋找這些native方法。

JNI有如下兩種注冊native方法的途徑:靜態和動態。其中:

靜態:先由Java得到本地方法的聲明,然后再通過JNI實現該聲明方法。

動態:先通過JNI重載JNI_OnLoad()實現本地方法,然后直接在Java中調用本地方法。

靜態注冊

根據函數名找到對應的JNI函數:Java層調用函數時,會從對應的JNI中尋找該函數,如果沒有就會報錯,如果存在則會建立一個關聯聯系,以后在調用時會直接使用這個函數,這部分的操作由虛擬機完成。

靜態方法就是根據函數名來遍歷java和jni函數之間的關聯,而且要求jni層函數的名字必須遵循

特定的格式。

具體的實現很簡單,首先在java代碼中聲明native函數,然后通過javah來生成native函數的具體形式,接下來在JNI代碼中實現這些函數即可。

看個例子就更明了了:

Java層:

static {

System.loadLibrary("samplelib_jni");

registerNatives();

}

private native void nativeFunc1();

private native void nativeFunc2();

private native void nativeFunc3();

接下來通過javah來產生jni代碼聲明:

假設你的java文件的包名是com.jni.samle 而類名是JniSample。

javah -d ./jni/ -classpath /Users/YOUR_NAME/Library/Android/sdk/platforms/android-21/android.jar:../../build/intermediates/classes/debug/ com.jni.samle.JniSample

然后就會得到一個JNI的.h文件,里面包含這幾個native函數的聲明,觀察一下文件名以及函數名,會有一定的規律,我會在下一篇文章中對此做一詳細介紹,在此不再贅述。

最后實現jni層的native方法即可。

動態注冊

對Java程序員來說,可能我們總是會遵循:1.編寫帶有native方法的Java類;—->2.使用javah命令生成.h頭文件;—->3.編寫代碼實現頭文件中的方法,這樣的標準流程,但也許有人無法忍受那“丑陋”的方法名稱,所以我們可以采用動態注冊方法,也就是通過RegisterNatives方法把c/c++中的方法隱射到Java中的native方法,而無需遵循特定的方法命名格式。

JNI 允許你提供一個函數映射表,注冊給Jave虛擬機,這樣Jvm就可以用函數映射表來調用相應的函數,

就可以不必通過函數名來查找需要調用的函數了。

Java與JNI通過JNINativeMethod的結構來建立聯系,它在jni.h中被定義,其結構內容如下:

typedef struct {

const char* name;

const char* signature;

void* fnPtr;

} JNINativeMethod;

第一個變量name是Java中函數的名字。

第二個變量signature,用字符串是描述了函數的參數和返回值

第三個變量fnPtr是函數指針,指向C函數。

當java通過System.loadLibrary加載完JNI動態庫后,緊接著會查找一個JNI_OnLoad的函數,如果有,就調用它, 而動態注冊的工作就是在這里完成的。

一起來看一下具體的實現方法:

Java code:

比較簡單,僅僅是加載so庫。

static {

System.loadLibrary("samplelib_jni");

}

JNI code:

在JNI中實現

jint JNI_OnLoad(JavaVM* vm, void* reserved)

并且在這個函數里面去動態的注冊native方法,完整的參考代碼如下:

#include

#include "Log4Android.h"

#include

#include

using namespace std;

#ifdef __cplusplus

extern "C" {

#endif

static const char *className = "com/zhixin/jnisample/JniManager";

static void sayHello(JNIEnv *env, jobject, jlong handle) {

LOGI("JNI", "native: say hello ###");

}

static JNINativeMethod gJni_Methods_table[] = {

{"sayHello", "(J)V", (void*)sayHello},

};

static int jniRegisterNativeMethods(JNIEnv* env, const char* className,

const JNINativeMethod* gMethods, int numMethods)

{

jclass clazz;

LOGI("JNI","Registering %s natives\n", className);

clazz = (env)->FindClass( className);

if (clazz == NULL) {

LOGE("JNI","Native registration unable to find class '%s'\n", className);

return -1;

}

int result = 0;

if ((env)->RegisterNatives(clazz, gJni_Methods_table, numMethods) < 0) {

LOGE("JNI","RegisterNatives failed for '%s'\n", className);

result = -1;

}

(env)->DeleteLocalRef(clazz);

return result;

}

jint JNI_OnLoad(JavaVM* vm, void* reserved){

LOGI("JNI", "enter jni_onload");

JNIEnv* env = NULL;

jint result = -1;

if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {

return result;

}

jniRegisterNativeMethods(env, className, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(JNINativeMethod));

return JNI_VERSION_1_4;

}

#ifdef __cplusplus

}

#endif

比較

下面我們來比較一下這兩種不同的實現方法。

首先是靜態方法:

優點實現起來比較簡單,直接用javah就能將Java代碼中的native函數的聲明轉化為native代碼的函數。

缺點在于:javah生成的jni層函數特別長;

初次調用native函數時要根據名字搜索對應的jni層函數來建立關聯聯系,這樣影響效率。

而動態方法的優缺點剛好和靜態方法相反。

通過上面的介紹我們發現,盡管靜態注冊實現起來比較簡單,但是會導致效率相對來說比較低。

所以在JNI層提供JNI_OnLoad是一個推薦的做法。如果不提供JNI_OnLoad,那么JNI函數的命名就需要符合規范,并且需要導出該函數。這樣做很容易被別人反編譯。相反,如果提供JNI_OnLoad,就可以在里面自己注冊JNI函數給Dalvik VM,這樣JNI函數的命名就可以按照自己的習慣了,而且也不用導出。

一個巧妙的合作

在實際的應用中,我們可以巧妙的將靜態注冊和動態注冊結合起來:也就是在java代碼中仍然聲明一個native函數,但是這個函數僅僅是用來去觸發在JNI層的native函數的動態注冊。說的有些繞,看看代碼就明白了:

Java層:

static {

System.loadLibrary("samplelib_jni");

registerNatives();

}

private static native void registerNatives();

JNI層:

通過javah生成java層聲明的native函數的文件,并且在實現代碼中去動態注冊JNI函數:

JNIEXPORT void JNICALL Java_com_zhixin_jni_JniSample_registerNatives

(JNIEnv *env, jclass clazz){

(env)->RegisterNatives(clazz, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(JNINativeMethod));

}

后續

關于JNI的函數注冊就到這里了,在下一篇文章中會總結JNI的簽名規則問題。

總結

以上是生活随笔為你收集整理的java类sample是公共的_应在名samle.java的文件_Andoid NDK编程 1 - 注册native函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。