- 簡介
JNI(Java Native Interface)顧名思義java本地接口,說白了就是java
和c/c++
通過JNI接口的規範實現通信,本文將實現最基本的java調c的函數
,c調java的函數
- 相關知識
C/C++基礎
編寫JAVA代碼
創建java文件 → 加載動態庫 → 定義本地函數 → 調用本地方法
package com.example.test;
public class MainActivity extends AppCompatActivity {
//加載動態庫
static {
System.loadLibrary("test");
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//調用本地方法
String str = javaCallNative("hello jni");
Log.i("xxxMainActivity", str + " --> javaCallNative");
}
/**
* 定義本地函數
*/
public native String javaCallNative(String str);
/**
* 定義被本地調用的函數
*/
public static void nativeCallJava(String str) {
Log.i("xxxMainActivity", str + " --> nativeCallJava");
}
}
編寫C代碼
根據jni規範,實現Java 中定義的函數
-
在main目錄下面,創建
jni
文件夾 -
在jni下面 , 創建
test.h
文件/** * 是否已經引用了test.h這個文件了,防止多次引用 * 一般是 * #ifndef XXX_H * #define XXX_H * * ... * * #endif * */ #ifndef TEST_H #define TEST_H #include <jni.h>//引用jni.h /** * 聲明方法,具體在同名.cpp裏面實現, * 注意函數名規範 -->Java_對應java方法的絕對路徑(JNIEnv *env , jobject p0 , 對應java方法的參數) */ extern "C" JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_javaCallNative(JNIEnv *env, jobject p0, jstring p1); #endif
-
在jni下面 , 創建
test.cpp
文件#include "test.h" /** * 定義c++函數 注意函數名規範 -->Java_對應java方法的絕對路徑(JNIEnv *env , jobject p0 , 對應java方法的參數) */ extern "C" JNIEXPORT jstring JNICALL Java_com_example_test_MainActivity_javaCallNative(JNIEnv *env, jobject jobj, jstring p1) { //通過反射調用java中的方法 //找class 使用FindClass方法,參數就是要調用的函數的類的完全限定名,但是需要把點換成/ jclass clazz = env->FindClass("com/example/test/MainActivity"); //獲取對應的函數: 參數1:類class,參數2:方法名,參數3:方法簽名 jmethodID method = env->GetStaticMethodID(clazz, "nativeCallJava", "(Ljava/lang/String;)V"); //調用返回空的靜態方法 env->CallStaticVoidMethod(clazz, method, p1); return p1; }
建立Android.mk文件
在jni下面 , 創建 Android.mk
文件,將本地C代碼編譯成.so的動態庫
#LOCAL_PATH定義本地路徑, $(call my-dir)是一個函數,意思是獲取當前函數
LOCAL_PATH := $(call my-dir)
#CLEAR_VARS由編譯系統提供,指定讓GNU MAKEFILE爲你清除許多LOCAL_XXX變量(例如 LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH 。這是必要的,因爲所有的編譯控制文件都在同一個GNU MAKE執行環境中,所有的變量都是全局的。
include $(CLEAR_VARS)
#LOCAL_MODULE變量必須定義,以標識你在Android.mk文件中描述的每個模塊。名稱必須是唯一的,而且不包含任何空格。注意編譯系統會自動產生合適的前綴和後綴,換句話說,一個被命名爲'foo'的共享庫模塊,將會生成'libfoo.so'文件。
LOCAL_MODULE := test
#LOCAL_SRC_FILES變量必須包含將要編譯打包進模塊中的C或C++源代碼文件。注意,你不用在這裏列出頭文件和包含文件,因爲編譯系統將會自動爲你找出依賴型的文件;僅僅列出直接傳遞給編譯器的源代碼文件就好。
LOCAL_SRC_FILES := test.cpp
#BUILD_SHARED_LIBRARY 由編譯系統提供,表示編譯一個分享庫(動態庫.so)
include $(BUILD_SHARED_LIBRARY)
建立Application.mk文件
指定 ndk-build 的項目範圍設置。默認情況下,它位於應用項目目錄中的 jni下
注意: Gradle 的 externalNativeBuild會忽略APP_ABI
注意: 使用 externalNativeBuild 進行編譯時,Android Studio 將根據您的編譯風格適當地設置此標記。
#Application Binary Interface編譯CPU架構
APP_ABI := armeabi
#指定目標Android API版本
APP_PLATFORM := android-14
#指定要使用的stl庫,默認爲system
APP_STL := system
#優化選項,默認值爲release。可選值爲debug和release。release模式下文件體積更小,debug模式下提供更多的調試信息
APP_OPTIM := release
在build.gradle 文件增加執行函數
在build.gradle
文件下增加執行函數,通過gradle自動編譯
android {
...
defaultConfig {
...
//設置編譯所支持的CPU架構
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
...
externalNativeBuild {
//編譯的Android.mk文件地址
ndkBuild {
path file("src/main/jni/Android.mk")
}
}
...
}
點擊sync now
當你修改了build.gradle文件,右上角就會出現sync now ,點擊就可以了
如果報沒有ndk,就需要添加ndk