自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Android架構(gòu)師之路:JNI與NDK編程-函數(shù)注冊(cè)與C++調(diào)用Java詳解(c++音視頻編碼基礎(chǔ))

開發(fā) 前端
這篇文章講解jni中函數(shù)的注冊(cè)和c++調(diào)用java的知識(shí)點(diǎn);JNI技術(shù)是Java世界與Native世界的通信橋梁,具體到代碼,Java層的代碼如何同Native層的代碼進(jìn)行調(diào)用的呢?

[[412937]]

前言小計(jì)

1、jni與ndk的基本知識(shí)點(diǎn)前面文章都講過了,不懂的,可以在公眾號(hào)首頁看;

2、jni中常用的方法比如:類、方法、數(shù)組、字符串等前面也講解過了;

3、這篇文章講解jni中函數(shù)的注冊(cè)和c++調(diào)用java的知識(shí)點(diǎn);

一. JNI函數(shù)注冊(cè)

圖片

1、jni函數(shù)注解知識(shí)點(diǎn)介紹

  • JNI技術(shù)是Java世界與Native世界的通信橋梁,具體到代碼,Java層的代碼如何同Native層的代碼進(jìn)行調(diào)用的呢?我們都知道,在調(diào)用native方法之前,首先要調(diào)用System.loadLibrary接口加載一個(gè)實(shí)現(xiàn)了native方法的動(dòng)態(tài)庫才能正常訪問,否則就會(huì)拋出java.lang.UnsatisfiedLinkError異常 。那么,在Java中調(diào)用某個(gè)native方法時(shí),JVM是通過什么方式,能正確的找到動(dòng)態(tài)庫中C/C++實(shí)現(xiàn)的那個(gè)native函數(shù)呢?
  • JVM查找native方法有兩種方式;
  • 按照J(rèn)NI規(guī)范的命名規(guī)則,調(diào)用JNI提供的RegisterNatives函數(shù),將本地函數(shù)注冊(cè)到JVM中;
  • 第一種方式,可用使用javah工具按照J(rèn)ava類中定義的native方法,按照J(rèn)NI規(guī)范的命名規(guī)則的方式自動(dòng)生成Jni本地C/C++頭文件;
  • 第二種方式則需要在本地庫的JNI_OnLoad函數(shù)中調(diào)用RegisterNatives來動(dòng)態(tài)注冊(cè);
  • JNI函數(shù)注冊(cè)是將Java層聲明的Native方法同實(shí)際的Native函數(shù)綁定起來的實(shí)現(xiàn)方式,也就是說,只要通過JNI函數(shù)注冊(cè)機(jī)制注冊(cè)了本地方,Java層就可以直接調(diào)用定義的這些本地方法了。對(duì)應(yīng)上述JVM查找native方法的兩種方式,JNI函數(shù)注冊(cè)方式一般分為靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)兩種方式。

2、靜態(tài)注冊(cè)

原理:根據(jù)函數(shù)名來建立 java 方法與 JNI 函數(shù)的一一對(duì)應(yīng)關(guān)系;

實(shí)現(xiàn)流程:

  • 編寫 java 代碼;
  • 利用 javah 指令生成對(duì)應(yīng)的 .h 文件;
  • 對(duì) .h 中的聲明進(jìn)行實(shí)現(xiàn);

弊端:

編寫不方便,JNI 方法名字必須遵循規(guī)則且名字很長(zhǎng);

編寫過程步驟多,不方便;

程序運(yùn)行效率低,因?yàn)槌醮握{(diào)用native函數(shù)時(shí)需要根據(jù)根據(jù)函數(shù)名在JNI層中搜索對(duì)應(yīng)的本地函數(shù),然后建立對(duì)應(yīng)關(guān)系,這個(gè)過程比較耗時(shí);

  1. public class Test { 
  2.     static { 
  3.         System.loadLibrary("native-lib"); 
  4.     } 
  5.     public native String textFromJni(); 
  6. 使用javah生成對(duì)應(yīng)的本地方法頭文件。 
  7. #include <jni.h> 
  8. #ifndef _Included_test 
  9. #define _Included_test 
  10. #ifdef __cplusplus 
  11. extern "C" { 
  12. #endif 
  13. JNIEXPORT jstring JNICALL Java_test_Test_textFromJni 
  14.   (JNIEnv *, jobject); 
  15. #ifdef __cplusplus 
  16. #endif 

3、動(dòng)態(tài)注冊(cè)

對(duì)應(yīng)與上面的靜態(tài)注冊(cè)方法,還有一種動(dòng)態(tài)注冊(cè)JNI函數(shù)的方式,即動(dòng)態(tài)注冊(cè)。動(dòng)態(tài)注冊(cè)是當(dāng)Java層調(diào)用System.loadLibrary方法加載so庫后,本地庫的JNI_OnLoad函數(shù)會(huì)被調(diào)用,在JNI_OnLoad函數(shù)中通過調(diào)用RegisterNatives函數(shù)來完成本地方法的注冊(cè)。

原理:利用 RegisterNatives 方法來注冊(cè) java 方法與 JNI 函數(shù)的一一對(duì)應(yīng)關(guān)系;

實(shí)現(xiàn)流程:

  • 利用結(jié)構(gòu)體 JNINativeMethod 數(shù)組記錄 java 方法與 JNI 函數(shù)的對(duì)應(yīng)關(guān)系;
  • 實(shí)現(xiàn) JNI_OnLoad 方法,在加載動(dòng)態(tài)庫后,執(zhí)行動(dòng)態(tài)注冊(cè);
  • 調(diào)用 FindClass 方法,獲取 java 對(duì)象;
  • 調(diào)用 RegisterNatives 方法,傳入 java 對(duì)象,以及 JNINativeMethod 數(shù)組,以及注冊(cè)數(shù)目完成注冊(cè);

優(yōu)點(diǎn):

流程更加清晰可控;

效率更高;

其中JNINativeMethod結(jié)構(gòu)體用來描述本地方法結(jié)構(gòu),其定義如下:

  1. typedef struct { 
  2.     const charname;      // Java方法名 
  3.     const char* signature; // Java方法簽名 
  4.     void* fnPtr;           // jni本地方法對(duì)應(yīng)的函數(shù)指針 
  5. } JNINativeMethod; 

結(jié)構(gòu)體的第一個(gè)參數(shù) name 是java 方法名;

第二個(gè)參數(shù) signature 用于描述方法的參數(shù)與返回值;

第三個(gè)參數(shù) fnPtr 是函數(shù)指針,指向 jni 函數(shù);

其中,第二個(gè)參數(shù) signature 使用字符串記錄方法的參數(shù)與返回值,具體格式形如“()V”、“(II)V”,其中分為兩部分,括號(hào)內(nèi)表示的是參數(shù),括號(hào)右側(cè)表示的是返回值;

①、數(shù)據(jù)類型映射

基本數(shù)據(jù)類型

②. 數(shù)組引用類型

如果是一維數(shù)組則遵循下表,如果是二維數(shù)組或更高維數(shù)組則對(duì)應(yīng)的 native 類型為 jobjectArray,域描述符中使用 ‘[’ 的個(gè)數(shù)表示維數(shù)

③. 對(duì)象引用類型

對(duì)于其它引用類型,即 java 中的對(duì)象,其映射規(guī)則為

④. 對(duì)象數(shù)組引用類型

如果是一維數(shù)組則遵循下表,如果是二維數(shù)組或更高維數(shù)組則對(duì)應(yīng)的 native 類型為 jobjectArray,域描述符中使用 ‘[’ 的個(gè)數(shù)表示維數(shù)

在Java文件中定義本地方法,加載本地so庫

  1. package test.jnitest; 
  2. public class Test { 
  3.     static { 
  4.         System.loadLibrary("native-lib"); 
  5.     } 
  6.     public native String textFromJni(); 

在JNI_OnLoad函數(shù)中注冊(cè)本地方法

  1. jstring textFromJni(JNIEnv* env, jobject thiz) { 
  2.     return env->NewStringUTF("text from jni"); 
  3. static JNINativeMethod gMethods[] = { 
  4.         {"textFromJni""()Ljava/lang/String;", (void*)textFromJni} 
  5. }; 
  6. int registerMethod(JNIEnv *env) { 
  7.     jclass test = env->FindClass("cc/ccbu/jnitest/Test"); 
  8.     return env->RegisterNatives(test, gMethods, sizeof(gMethods)/ sizeof(gMethods[0])); 
  9. JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { 
  10.     JNIEnv* env = NULL
  11.     if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) { 
  12.         return JNI_ERR; 
  13.     } 
  14.     if (registerMethod(env) != JNI_OK) { 
  15.         return JNI_ERR; 
  16.     } 
  17.     return  JNI_VERSION_1_6; 

注意:

在JNI_OnLoad函數(shù)的結(jié)尾處,我們一定要有返回值,而且必須是JNI_VERSION_1_4 或 JNI_VERSION_1_6,也就是JNI的版本號(hào),我們一定要返回正確的版本號(hào),否則系統(tǒng)也是無法加載的;

4、c++調(diào)用java詳解

(1) 找到j(luò)ava對(duì)應(yīng)的Class

(2) 找到要調(diào)用的方法的methodID

(3) 在C語言中調(diào)用相應(yīng)方法

①.通過JAVA層的本地方法創(chuàng)建同類對(duì)象

步驟:

I.通過對(duì)象獲取類

II.通過類獲取類的構(gòu)造方法的ID

III.基于方法ID和類,創(chuàng)建新對(duì)象

  1. JNIEXPORT void JNICALL JAVA_nativeMethod 
  2.         (JNIEnv *env, jobject thiz,jint i){ 
  3.     ... 
  4.     jclass clazz = (*env).GetObjectClass(thiz); 
  5.     jmethodID mid = (*env).GetMethodID(clazz,"<init>","()V"); 
  6.     jobject obj = (*env).NewObject(clazz,mid); 
  7.     ... 
  8.     return

②.通過C/C++創(chuàng)建不同類對(duì)象

步驟:

I.通過FindClass方法獲取需要的類

II.通過類獲取類的構(gòu)造方法的ID

III.基于方法ID和類,創(chuàng)建新對(duì)象

  1. JNIEXPORT void JNICALL JAVA_nativeMethod 
  2.         (JNIEnv *env, jobject thiz,jint i){ 
  3.     ... 
  4.     jclass clazz = (*env).FindClass("com/x/test/Test");//參數(shù)為類路徑 
  5.     jmethodID mid = (*env).GetMethodID(clazz,"<init>","()V"); 
  6.     jobject obj = (*env).NewObject(clazz,mid); 
  7.     ... 
  8.     return

③獲取上下文環(huán)境JNIEnv

如果找不到上下文JNIEnv就要獲取

  1. bool AttachCurrentThread(JavaVM* vm, JNIEnv** p_env) 
  2.     bool bAttached = false
  3.  
  4.     switch(vm->GetEnv((void**)p_env, JNI_VERSION_1_4)) 
  5.     { 
  6.         case JNI_OK: 
  7.             break; 
  8.         case JNI_EDETACHED: 
  9.  
  10.             if (vm->AttachCurrentThread(p_env, 0) < 0) 
  11.             { 
  12.                 LOGD("%s :test failed!",__func__); 
  13.                 return false
  14.             } 
  15.             else 
  16.             { 
  17.                 bAttached = true
  18.             } 
  19.             break; 
  20.         case JNI_EVERSION: 
  21.             LOGE("Invalid java version"); 
  22.             break; 
  23.     } 
  24.   return bAttached; 
  25.  

總結(jié)

以上總結(jié)了JNI中函數(shù)注冊(cè)的兩種方法,在實(shí)際應(yīng)用中都很常見都用得到的,要理解到位才可以;

下次文章會(huì)繼續(xù)講解關(guān)于JNI的知識(shí)點(diǎn)和高級(jí)應(yīng)用

本文轉(zhuǎn)載自微信公眾號(hào)「Android開發(fā)編程」,可以通過以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系A(chǔ)ndroid開發(fā)編程公眾號(hào)。

 

責(zé)任編輯:姜華 來源: Android開發(fā)編程
相關(guān)推薦

2021-07-20 05:34:45

JNINDKC++

2011-07-01 14:55:28

Qt QML C++

2012-04-28 15:28:21

JNI混合編程Java

2009-07-20 09:53:43

Java混合編程

2010-01-19 15:36:02

C++語言

2011-08-04 13:38:01

Objective-C C++

2012-03-20 11:37:24

JavaJNI

2010-01-28 13:35:41

調(diào)用C++函數(shù)

2025-04-02 03:11:00

Python函數(shù)C++

2010-01-25 09:57:39

C++函數(shù)參數(shù)

2011-08-22 17:13:00

LuaC++函數(shù)

2010-01-11 10:28:51

C++編程

2010-01-21 11:23:58

C++函數(shù)調(diào)用

2010-02-01 16:13:15

C++繼承

2010-02-02 15:59:32

C++賦值函數(shù)

2010-01-12 17:55:03

C++程序

2009-08-13 17:30:30

C#構(gòu)造函數(shù)

2011-07-14 17:45:06

CC++

2011-07-15 00:47:13

C++多態(tài)

2010-01-11 14:17:02

C++編程
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)