在 windows 系統上面咱們常常能看到不少相似於 xxx.dll 的文件,在作 android 開發的時候咱們能看到不少 xxx.so 的文件。這些都是啥呢?其實就是用 c 和 c++ 實現生成的動態庫,供 windows 和 android 系統來調用。java
咱們解壓 QQ 和支付寶的 apk 找到它的 libs 目錄下,會發現有大量的 .so 庫文件,還有不少是動態下載加載的咱們看不到,能看到的就已經有好幾百個了。爲何要這麼弄?直接就用 java 開發不行嗎?其實好處有不少,好比安全,高效,跨平臺等等。今天咱們就來看下 JNI 開發的通常開發流程。android
1.1 編寫 native 方法c++
public class NdkTest {
public static void main(String[] args) {
NdkTest ndkTest = new NdkTest();
System.out.println("簽名密鑰:"+ndkTest.getSignaturePassword());
}
// 獲取簽名密鑰
public native String getSignaturePassword();
static{
// 加載某個路徑下的動態庫
System.load("C:/Users/hcDarren/Desktop/android/NDK/NDK_Day12/x64/Debug/NDK_Day12.dll");
}
}
複製代碼
1.2 生成 xxx.h 頭文件編程
javah -d ../jni -jni com.darren.ndk12.NdkTest
複製代碼
1.3 VS 編寫實現方法生成 dll 動態庫windows
// 引入頭文件
#include "com_darren_ndk12_NdkTest.h"
JNIEXPORT jstring JNICALL Java_com_darren_ndk12_NdkTest_getSignaturePassword
(JNIEnv *env, jobject jobj){
return (*env)->NewStringUTF(env,"940223");
}
複製代碼
/* DO NOT EDIT THIS FILE - it is machine generated */
#include "jni.h" // 引入頭 jni.h 文件
/* Header for class com_darren_ndk12_NdkTest */
// 打個標記,防止反覆引入 copy 內容
#ifndef _Included_com_darren_ndk12_NdkTest
#define _Included_com_darren_ndk12_NdkTest
#ifdef __cplusplus
// 若是是 c++ 則統一用 C 的編譯方式
// 會指示編譯器這部分代碼按C語言的進行編譯,而不是C++的。
// C語言並不支持函數重載,所以編譯C語言代碼的函數時不會帶上函數的參數類型,通常只包括函數名。
extern "C" {
#endif
/*
* Class: com_darren_ndk12_NdkTest
* Method: getSignaturePassword
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_darren_ndk12_NdkTest_getSignaturePassword
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
複製代碼
// 引入頭文件
#include "com_darren_ndk12_NdkTest.h"
// JNIEXPORT:在Jni編程中全部本地語言實現Jni接口的一個標誌
// jstring:對應 java 中的數據類型 String
// JNICALL:也是一個標記能夠去掉,編譯運行也不會有問題
// JNIEnv:c 與 java 相互調用的橋樑,它提供了不少函數方法
// jobj:java 傳遞下來的對象,即上面的 NdkTest
JNIEXPORT jstring JNICALL Java_com_darren_ndk12_NdkTest_getSignaturePassword
(JNIEnv *env, jobject jobj){
// 經過 JNIEnv 把 c 字符串轉爲 jstring
return (*env)->NewStringUTF(env,"940223");
}
複製代碼
####三.JNIEnv 的實現原理 JNI 的基礎學習咱們主要搞清 JNIEnv 就能夠了,只要熟悉它裏面的函數方法。可是學習的時候確定不光要搞清它的方法函數,還須要搞清它的實現原理。若是有留意我以前寫的一些文章你會發現 c 和 c++ 有所不一樣,c++ 是這樣的。安全
JNIEXPORT jstring JNICALL Java_com_darren_ndk12_NdkTest_getSignaturePassword
(JNIEnv *env, jobject jobj){
return env -> NewStringUTF("940223");
}
複製代碼
爲何會有這樣的不一樣?c++ 的實現方式咱們後面的文章再去詳細講解,咱們先來模擬一下 c 的實現方式:bash
// 引入頭文件
#include <stdio.h>
// 定義結構體指針別名
typedef const struct JNINativeInterface *JNIEnv;
// 定義一個結構體
struct JNINativeInterface{
// 定義的不是函數,函數指針
char*(*NewStringUTF)(JNIEnv*, char*);
};
// 只是作一個模擬
char* Java_com_darren_ndk12_NdkTest_getSignaturePassword(JNIEnv *env){
return (*env)->NewStringUTF(env, "940223");
}
// NewStringUTF 方法的實現
char* NewStringUTF(JNIEnv* jniEnv, char* str){
// 一連串的實現 char* -> jstring
return str;
}
void main(){
// 中間還會有不少的流程,咱們能看到的就是調用 Java_com_darren_ndk12_NdkTest_getSignaturePassword
// 模擬 JNIEnv 建立過程
struct JNINativeInterface nativeInterface;
nativeInterface.NewStringUTF = NewStringUTF;
JNIEnv jniEnv = &nativeInterface;// 一級指針
// Java_com_darren_ndk12_NdkTest_getSignaturePassword 參數須要的是 JNIEnv*
JNIEnv* env = &jniEnv;// 雖然只有一個 * ,可是其實他是一個二級指針
char* singnature = Java_com_darren_ndk12_NdkTest_getSignaturePassword(env);
printf("singnature = %s", singnature);
// 而後將 jstring 返回給 java
getchar();
}
複製代碼
視頻連接:https://pan.baidu.com/s/1vyxCSn0SWo3-YnoD7Rzryw 視頻密碼:uqmc函數