NDK
一、概念
1.1 ndk(原生開發套件)是一套工具,能夠讓開發者在Android應用中使用c和c++代碼。
1.2 使用場景。
★進一步提升設備性能,以降低延遲,或運行計算密集型應用,如游戲或物理模擬
★重復使用自己過其他開發者的c和c++庫
**
二、編譯方式
**
2.1 ndkbuild ,as2.2之后,默認Cmake為編譯構建工具。ndkbuild,需要的配置文件:android.mk, application.mk ,這種編譯方式較為傳統,必須遵守Java-包名-類名-方法名(JniEnv *env, jobect jobj,參數……)
在gradle中配置:
externalNativeBuild {ndkBuild {path "src/main/jni/Android.mk"} }此外,還可以android->defaultConfig 中配置加載動態庫的平臺
ndk{abiFilters "armeabi-v7a"……等 }2.2 cmake, 這種方式也是as默認的構建編譯方式。相比于ndkbuild,可以不遵循復雜的命名方式,但需與Java類名保持一致。需要的配置文件: CMakeLists.txt
在gradle中配置:
externalNativeBuild {cmake {path "CMakeLists.txt"} }配置加載動態庫的平臺:
externalNativeBuild {cmake {abiFilters "armeabi-v7a"cppFlags ""} }**
三、注冊方式
**
3.1 靜態注冊
1. 原理: 根據函數名來建立 java 方法與 JNI 函數的一一對應關系。通過 JNIEXPORT 和 JNICALL 兩個宏定義聲明,在虛擬機加載 so 時發現上面兩個宏定義的函數時就會鏈接到對應的 native 方法,找到對應的方法時,保存jni對應函數的指針。
2. 命名規則:Java + 包名 + 類名 + 方法名,一般使用javah 編譯.class 文件生成頭文件,再進行對相應的方法實現。
3.優點: 明了
4.缺點:
*方法命名過長
*必須遵循命名規則
*首次調用native 函數時需要根據函數名字搜索對應的Jni層函數來建立關聯關系,這樣會影響運行效率。
3.2 動態注冊
1.原理: 當java層通過System.loadLibrary 加載完JNI動態庫后,緊接著會查找該庫中一個叫JNI_OnLoad的函數,并在JNI_OnLoad中通過 RegisterNatives 方法手動完成 native 方法和 so 中的方法的綁定, 完成動態注冊。
#include <jni.h> #include <string> #include "log.hpp"extern "C" {jstring stringFromJNI(JNIEnv *env, jobject instance) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str()); }jint add(JNIEnv *env, jclass clazz, jint a, jint b) {return a + b; }jint RegisterNatives(JNIEnv *env) {jclass clazz = env->FindClass("com/afei/jnidemo/MainActivity");if (clazz == NULL) {LOGE("con't find class: com/afei/jnidemo/MainActivity");return JNI_ERR;}JNINativeMethod methods_MainActivity[] = {{"stringFromJNI", "()Ljava/lang/String;", (void *) stringFromJNI},{"add", "(II)I", (void *) add}};// int len = sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0]);return env->RegisterNatives(clazz, methods_MainActivity,sizeof(methods_MainActivity) / sizeof(methods_MainActivity[0])); }jint JNI_OnLoad(JavaVM *vm, void *reserved) {JNIEnv *env = NULL;if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;}jint result = RegisterNatives(env);LOGD("RegisterNatives result: %d", result);return JNI_VERSION_1_6; }}2.JNINativeMethod
typedef struct {const char* name; // native 的方法名const char* signature; // 方法簽名,例如 ()Ljava/lang/String;void* fnPtr; // 函數指針 } JNINativeMethod;四、數據類型轉換
基本數據類型可以直接與C/C++的相應基本數據類型映射,JNI用類型定義使得這種映射對開發人員透明
| Boolean | Jblloean | unsigned char | 無符號8位 |
| Byte | Jbyte | char | 有符號8位 |
| Char | Jchar | unsigned short | 無符號16位 |
| Short | Jshort | short | 有符號16位 |
| Int | Jint | int | 有符號32位 |
| Long | Jlong | long long | 有符號64位 |
| Float | Jfloat | float | 32位 |
| Double | Jdouble | double | 64位 |
2 引用數據類型
與基本數據類型不同,引用類型對原生方法是不透明的,它們內部的數據結構并不直接向原生代碼公開
| java.lang.Class | jclass |
| java.lang.Throwable | jthorwable |
| java.lang.String | jstring |
| Other objects | jobjects |
| java.lang.Object[] | jobjectArray |
| boolean[] | jbooleanArray |
| byte[] | jbooleanArray |
| char[] | jcharArray |
| short[] | jshortArray |
| int[] | jintArray |
| long[] | jlongArray |
| float[] | jfloatArray |
| double[] | jdoubleArray |
| Other arrays | Jarray |
總結
- 上一篇: Android Studio 将项目转变
- 下一篇: kotlin 从Helloword 开始