java jni技术_JNI技术简介
JNI(Java Native Interface)
提供一種Java字節碼調用C/C++的解決方案,JNI描述的是一種技術。
NDK(Native Development Kit)
Android NDK 是一組允許您將 C 或 C++(“原生代碼”)嵌入到 Android 應用中的工具,NDK描述的是工具集。 能夠在 Android 應用中使用原生代碼對于想執行以下一項或多項操作的開發者特別有用:
在平臺之間移植其應用。
重復使用現有庫,或者提供其自己的庫供重復使用。
在某些情況下提高性能,特別是像游戲這種計算密集型應用。
JNI方法注冊
靜態注冊
當Java層調用navtie函數時,會在JNI庫中根據函數名查找對應的JNI函數。如果沒找到,會報錯。如果找到了,則會在native函數與JNI函數之間建立關聯關系,其實就是保存JNI函數的函數指針。下次再調用native函數,就可以直接使用這個函數指針。
JNI函數名格式(需將”.”改為”_”):
Java_ + 包名(com.example.auto.jnitest)+ 類名(MainActivity) + 函數名(stringFromJNI)
靜態方法的缺點:
要求JNI函數的名字必須遵循JNI規范的命名格式;
名字冗長,容易出錯;
初次調用會根據函數名去搜索JNI中對應的函數,會影響執行效率;
需要編譯所有聲明了native函數的Java類,每個所生成的class文件都要用javah工具生成一個頭文件;
動態注冊
通過提供一個函數映射表,注冊給JVM虛擬機,這樣JVM就可以用函數映射表來調用相應的函數,就不必通過函數名來查找需要調用的函數。
Java與JNI通過JNINativeMethod的結構來建立函數映射表,它在jni.h頭文件中定義,其結構內容如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
創建映射表后,調用RegisterNatives函數將映射表注冊給JVM;
當Java層通過System.loadLibrary加載JNI庫時,會在庫中查JNI_OnLoad函數。可將JNI_OnLoad視為JNI庫的入口函數,需要在這里完成所有函數映射和動態注冊工作,及其他一些初始化工作。
數據類型轉換
基礎數據類型轉換
引用數據類型轉換
除了Class、String、Throwable和基本數據類型的數組外,其余所有Java對象的數據類型在JNI中都用jobject表示。Java中的String也是引用類型,但是由于使用頻率較高,所以在JNI中單獨創建了一個jstring類型。
引用類型不能直接在 Native 層使用,需要根據 JNI 函數進行類型的轉化后,才能使用;
多維數組(含二維數組)都是引用類型,需要使用 jobjectArray 類型存取其值;
例如,二維整型數組就是指向一位數組的數組,其聲明使用方式如下:
//獲得一維數組的類引用,即jintArray類型
jclass intArrayClass = env->FindClass("[I");
//構造一個指向jintArray類一維數組的對象數組,該對象數組初始大小為length,類型為 jsize
jobjectArray obejctIntArray = env->NewObjectArray(length ,intArrayClass , NULL);
JNI函數簽名信息
由于Java支持函數重載,因此僅僅根據函數名是沒法找到對應的JNI函數。為了解決這個問題,JNI將參數類型和返回值類型作為函數的簽名信息。
JNI規范定義的函數簽名信息格式:
(參數1類型字符…)返回值類型字符
函數簽名例子:
JNI常用的數據類型及對應字符:
JNIEnv介紹
JNIEnv概念 :
JNIEnv是一個線程相關的結構體, 該結構體代表了 Java 在本線程的運行環境。通過JNIEnv可以調用到一系列JNI系統函數。
JNIEnv線程相關性:
每個線程中都有一個 JNIEnv 指針。JNIEnv只在其所在線程有效, 它不能在線程之間進行傳遞。
注意:在C++創建的子線程中獲取JNIEnv,要通過調用JavaVM的AttachCurrentThread函數獲得。在子線程退出時,要調用JavaVM的DetachCurrentThread函數來釋放對應的資源,否則會出錯。
JNIEnv 作用:
訪問Java成員變量和成員方法;
調用Java構造方法創建Java對象等。
JNI編譯
ndkBuild
Cmake編譯
CMake 則是一個跨平臺的編譯工具,它并不會直接編譯出對象,而是根據自定義的語言規則(CMakeLists.txt)生成 對應 makefile 或 project 文件,然后再調用底層的編譯, 在Android Studio 2.2 之后支持Cmake編譯。
add_library 指令
語法:add_library(libname [SHARED | STATIC | MODULE] [EXCLUDE_FROM_ALL] [source])
將一組源文件 source 編譯出一個庫文件,并保存為 libname.so (lib 前綴是生成文件時 CMake自動添加上去的)。其中有三種庫文件類型,不寫的話,默認為 STATIC;
SHARED: 表示動態庫,可以在(Java)代碼中使用 System.loadLibrary(name) 動態調用;
STATIC: 表示靜態庫,集成到代碼中會在編譯時調用;
MODULE: 只有在使用 dyId 的系統有效,如果不支持 dyId,則被當作 SHARED 對待;
EXCLUDE_FROM_ALL: 表示這個庫不被默認構建,除非其他組件依賴或手工構建;
#將compress.c 編譯成 libcompress.so 的共享庫
add_library(compress SHARED compress.c)
target_link_libraries 指令
語法:target_link_libraries(target library library2…)
這個指令可以用來為 target 添加需要的鏈接的共享庫,同樣也可以用于為自己編寫的共享庫添加共享庫鏈接。如:
#指定 compress 工程需要用到 libjpeg 庫和 log 庫
target_link_libraries(compress libjpeg ${log-lib})
find_library 指令
語法:find_library( name1 path1 path2 ...)
VAR 變量表示找到的庫全路徑,包含庫文件名 。例如:
find_library(libX X11 /usr/lib)
find_library(log-lib log) #路徑為空,應該是查找系統環境變量路徑
Abi架構
ABI(Application binary interface)應用程序二進制接口。不同的CPU 與指令集的每種組合都有定義的 ABI (應用程序二進制接口),一段程序只有遵循這個接口規范才能在該 CPU 上運行,所以同樣的程序代碼為了兼容多個不同的CPU,需要為不同的 ABI 構建不同的庫文件。當然對于CPU來說,不同的架構并不意味著一定互不兼容。
armeabi設備只兼容armeabi;
armeabi-v7a設備兼容armeabi-v7a、armeabi;
arm64-v8a設備兼容arm64-v8a、armeabi-v7a、armeabi;
X86設備兼容X86、armeabi;
X86_64設備兼容X86_64、X86、armeabi;
mips64設備兼容mips64、mips;
mips只兼容mips;
根據以上的兼容總結,我們還可以得到一些規律:
armeabi的SO文件基本上可以說是萬金油,它能運行在除了mips和mips64的設備上,但在非armeabi設備上運行性能還是有所損耗;
64位的CPU架構總能向下兼容其對應的32位指令集,如:x86_64兼容X86,arm64-v8a兼容armeabi-v7a,mips64兼容mips;
問題排查 addr2line
03-21 23:59:32.032 6770-6770/? A/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
03-21 23:59:32.032 6770-6770/? A/DEBUG: Build fingerprint: 'google/sdk_gphone_x86/generic_x86:8.1.0/OPM1.171004.001/4376136:user/release-keys'
03-21 23:59:32.032 6770-6770/? A/DEBUG: Revision: '0'
03-21 23:59:32.032 6770-6770/? A/DEBUG: ABI: 'x86'
03-21 23:59:32.032 6770-6770/? A/DEBUG: pid: 6745, tid: 6745, name: ucai.nativedemo >>> com.choufucai.nativedemo <<<
03-21 23:59:32.032 6770-6770/? A/DEBUG: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x70
03-21 23:59:32.032 6770-6770/? A/DEBUG: Cause: null pointer dereference
03-21 23:59:32.032 6770-6770/? A/DEBUG: eax 00000070 ebx a8a6479c ecx 00000035 edx 00000075
03-21 23:59:32.032 6770-6770/? A/DEBUG: esi ffffffff edi ffffffff
03-21 23:59:32.032 6770-6770/? A/DEBUG: xcs 00000073 xds 0000007b xes 0000007b xfs 0000003b xss 0000007b
03-21 23:59:32.032 6770-6770/? A/DEBUG: eip a89a2553 ebp bffa2408 esp bffa1e78 flags 00010202
03-21 23:59:32.228 6770-6770/? A/DEBUG: backtrace:
03-21 23:59:32.228 6770-6770/? A/DEBUG: #00 pc 0001d553 /system/lib/libc.so (strlen+51)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #01 pc 0005fd5d /system/lib/libc.so (__vfprintf+5581)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #02 pc 0008439e /system/lib/libc.so (vsnprintf+222)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #03 pc 00022f30 /system/lib/libc.so (__vsnprintf_chk+48)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #04 pc 000068de /system/lib/liblog.so (__android_log_print+78)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #05 pc 00000ee2 /data/app/com.choufucai.nativedemo-c_F0BwkNYJA0ITdueTXEdg==/lib/x86/libnative-lib.so
03-21 23:59:32.228 6770-6770/? A/DEBUG: #06 pc 00647e67 /system/lib/libart.so (art_quick_generic_jni_trampoline+71)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #07 pc 00641e62 /system/lib/libart.so (art_quick_invoke_stub+338)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #08 pc 00115fdf /system/lib/libart.so (art::ArtMethod::Invoke(art::Thread*, unsigned int*, unsigned int, art::JValue*, char const*)+223)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #09 pc 0032143f /system/lib/libart.so (art::interpreter::ArtInterpreterToCompiledCodeBridge(art::Thread*, art::ArtMethod*, art::ShadowFrame*, unsigned short, art::JValue*)+335)
03-21 23:59:32.228 6770-6770/? A/DEBUG: #10 pc 0031a6a4 /system/lib/libart.so
以上錯誤日志中backtrace就是堆棧信息,#00 #01 就是堆棧列表。 #00 就是堆棧頂層即是錯誤所在地址,pc后面的就是地址,可以通過以下命令查找出地址可以獲得對應的源碼文件和行號:
// -f 輸出函數名
// -e 輸出錯誤代碼行數和文件路徑
// xxx.so 對應出錯的so文件, 在android工程obj目錄下
// addr 是具體的地址
arm-linux-androideabi-addr2line -f -e xxx.so addr
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的java jni技术_JNI技术简介的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: c语言Max错误,C语言编程常见错误.p
- 下一篇: 文件上传控件 css,CSS3 自定义文