Android項目中實現native調用


轉載自搜狗測試公眾號,本人學習使用,侵權刪

最近小編在做公司輸入法項目中java與native交互部分的測試,先簡單學習了java代碼調用native代碼的實現原理,本次與大家一起分享jni協議,了解java關聯C/C++代碼的調用原則。

JNI是Java Native Interface的縮寫,能夠提供API實現Java和Native語言(主要是C/C++)的通信,JNI提供兩種方式實現Java對native代碼的調用:靜態關聯和動態關聯。

靜態關聯

靜態關聯的實現過程是通過經過特定規則命名的jni函數名來遍歷java和jni函數之間的關聯。具體分三步實現:

1、java代碼中聲明native函數;

2、通過javah生成native函數的jni形式;

3、在jni代碼中實現native函數。

示例如下:

1、實現一段java代碼JNIUtils.java:

package com.example.administrator.myapplication;

public class JNIUtils {    
   static{        System.loadLibrary("native-lib");    }    
   public static native String sayHiFromJNI(); }

JNIUtils.java代碼包名為com.example.administrator.myapplication,聲明了native函數名為sayHiFromJNI()。

2、通過javah生成native函數的jni形式

在代碼的src/main/java目錄下通過terminal端輸入命令:javah -d ../jni com.example.administrator.myapplication.JNIUtils。通過Javah命令能夠生成java類對應的頭文件,命令-d表示生成一個目錄,習慣上我們會將jni相關代碼存放在java同級目錄下的jni文件夾中(../jni),最后的com.example.administrator.myapplication.JNIUtils就是我們的JNIUtils完整類名了。

執行后jni目錄下會生成一個com.example.administrator.myapplication.JNIUtils.h文件,如下:

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

#ifndef _Included_com_example_administrator_myapplication_JNIUtils
#define _Included_com_example_administrator_myapplication_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif/* * Class:     com_example_administrator_myapplication_JNIUtils * Method:    sayHiFromJNI * Signature: ()Ljava/lang/String; */
JNIEXPORT jstring JNICALL Java_com_example_administrator_myapplication_JNIUtils_sayHiFromJNI  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

3、在jni代碼中實現native函數

在jni目錄中新建cpp文件,命名JNIHi.cpp,在cpp文件中include "com_example_administrator_myapplication_JNIUtils.h"實現native函數的功能即可,在JNIUtils.java文件中我們定義了public static native String sayHiFromJNI();函數,因此在JNIHi.cpp中需要實現具體邏輯。

代碼如下:

#include "com_example_administrator_myapplication_JNIUtils.h"

JNIEXPORT jstring JNICALL Java_com_example_administrator_myapplication_JNIUtils_sayHiFromJNI        
       (JNIEnv *env, jclass jclass){    
   return env->NewStringUTF("Hi From JNI!!!");
}

如此便實現了JNIUtils.java代碼中對C++代碼JNIHi.cpp中函數的調用。

動態關聯

靜態關聯的方法簡單易學,但是是不是有人覺得函數名這么長,規范是否太繁瑣,那么我們還有更簡單的方式:動態關聯。

動態方式的主要實現原理是通過RegisterNatives函數把C/C++中的方法映射到Java中。

1、編寫java代碼JNIUtils.java,與靜態關聯相同

package com.example.administrator.myapplication;

public class JNIUtils {    
   static{        System.loadLibrary("native-lib");    }    
   public static native String sayHiFromJNI(); }

上述函數中我們使用System.loadLibrary("native-lib")方法加載so庫的時候,Java虛擬機就會找到JNI_OnLoad函數並調用,該函數前面有三個關鍵字分別是JNIEXPORT,JNICALL ,jint。其中JNIEXPORT和JNICALL是兩個宏定義,用於指定該函數是JNI函數,通過該函數能夠實現java與native的動態關聯,以代碼示例。

2、編寫native關聯代碼JNIHi.cpp

代碼示例:

#include <jni.h>
#include <stdio.h>
#include<android/log.h>
#include <stdlib.h>

using namespace std;
#ifdef __cplusplus
extern "C" {
#endif

static const char *className = "com/example/administrator/myapplication/JNIUtils";

JNIEXPORT jstring JNICALL sayHiFromJNI(JNIEnv *env,jobject obj) {    
   return env->NewStringUTF("Hi From JNI!!!"); }

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

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){    JNIEnv* env = NULL;    jint result = -1;    
   if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        
       return result;    }    jclass clazz = (env)->FindClass( className);    
   if (clazz == NULL){        
       return -1;    }    
   
   if ((env)->RegisterNatives(clazz, gJni_Methods_table, sizeof(gJni_Methods_table) / sizeof(gJni_Methods_table[0])) < 0)    {        
       return -1;    }    
   
   return JNI_VERSION_1_4; }

#ifdef __cplusplus
}
#endif

通過代碼閱讀,我們發現JNI_OnLoad函數的實現主要包含兩步:第一、vm->GetEnv()函數獲取JNIEnv結構體指針,該指針指向一個函數表,對應JNI函數,我們可以通過這些JNI函數實現JNI編程;第二、RegisterNatives()函數實現native方法的注冊,其中主要應用了一個靜態變量JNINativeMethod類型的數組,它代表了native方法。JNINativeMethod結構被定義在jni.h中,Java與JNI可以通過該結構建立聯系,如此Java虛擬機就可以用相應的函數映射表來調用相應的函數,而不需要通過函數名來查找需要調用的函數了。

小結

簡而言之,靜態關聯:先由Java聲明本地方法,然后通過JNI實現方法的定義。動態關聯:先通過JNI_OnLoad實現本地方法,然后直接在Java中調用。兩種方法各有優缺點,大家根據自己的代碼習慣選擇合適的方式就好。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM