1. Java調用C庫中函數的步驟
詳細使用請參照《jni.pdf》官方文檔
(1)加載C庫:
在java中:加載C庫,並聲明在C庫中實現的本地方法: System.loadLibrary("libhello");
就會生成對應的文件:com_visionvera_LTE4GDataTransport_Native_Native.h
其中就有你要的函數簽名了!!!
② 之後就可以點擊工具欄上的External Tools按鈕生成頭文件了(要先定位到包含本地方法的類文件)
生成的頭文件在工程的jni目錄中,如果沒有顯示出來,刷新下即可
(1)java代碼
// MainActivity.java
package com.example.jni_test;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
// 1. 加載C庫: 通常放在靜態代碼塊中,因爲靜態代碼塊是在構造對象之前執行, 並且只執行一次, 放在這裏比較合適
static {
System.loadLibrary("native"); // libnative.so
}
// 2. 聲明本地方法
public static native String native_testString(String s);
public static native int[] native_testArray(int[] arr);
public static native int[] native_testArray(int length);
public native void native_cCallJava();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 3. 調用本地方法
// 3.1 java傳遞String給C, C返回String給java
String str = native_testString("abcd");
System.out.println(str);
// 3.2 java傳遞數組給C, C處理後再返回給java
int[] arr = { 1, 2, 3 };
int[] retArr;
retArr = native_testArray(arr);
for (int i = 0; i < retArr.length; i++) {
if (i == retArr.length - 1) {
System.out.println(retArr[i]);
} else {
System.out.print(retArr[i] + ", ");
}
}
// 3.3 C中構建數組, 然後返回給java
retArr = native_testArray(5);
for (int i = 0; i < retArr.length; i++) {
if (i == retArr.length - 1) {
System.out.println(retArr[i]);
} else {
System.out.print(retArr[i] + ", ");
}
}
// 3.4 C中直接調用java中的方法
native_cCallJava();
}
public void showDialog(String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("警告!!");
builder.setMessage(message);
builder.show();
}
public void showDialog(String message, int i) {}
}
(2) jni代碼
// native.c
#include <jni.h>
#include <cutils/log.h>
#include <assert.h>
#define LOG_TAG "NATIVE"
jstring native_testString(JNIEnv *env, jclass clazz, jstring jstr) {
ALOGI("Call native_testString\n");
/*
* 注意:java傳遞過來的其實是一個String對象,這裏使用jstring,也就是void*來接收
* 要通過GetStringUTFChars從jstring轉來解析出c語言中的char*
* const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*);
*/
char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL); //這裏會分配空間
if (cstr == NULL) {
return 0; /* out of memory */
}
for(int i = 0; i < strlen(cstr); i++) { //把每個字符取出來, +1
cstr[i] = cstr[i] + 1;
}
//jstring (*NewStringUTF)(JNIEnv*, const char*);
jstring jstr_to_return = (*env)->NewStringUTF(env, cstr); //其實這裏是轉換成了一個String對象, 賦值給jstring, 返回, java中使用String接收即可
(*env)->ReleaseStringUTFChars(env, jstr, cstr); // 將上面分配的空間釋放掉
return jstr_to_return;
}
jintArray native_testArray1(JNIEnv *env, jclass clazz, jintArray jArray) {
ALOGI("Call native_testArray1\n");
jint *carr;
jintArray jarr_to_return;
/**
* 注意:這裏的jArray是java傳遞過來的, 它是java的數組, 並不是C的數組
* JNIEnv提供的並不將講java的數組轉換成C的數組 而是如下:
* jsize (*GetArrayLength)(JNIEnv*, jarray);
*/
//jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
carr = (*env)->GetIntArrayElements(env, jArray, 0);
if (carr == NULL) {
return NULL; /* exception occurred */
}
int len = (*env)->GetArrayLength(env, jArray); // 獲取數組長度
for (int i = 0; i < len; i++) {
carr[i] = carr[i] + 10;
}
(*env)->SetIntArrayRegion(env, jArray, 0, len, carr);
(*env)->ReleaseIntArrayElements(env, jArray, carr, 0); //別忘了釋放
return jArray;
}
jintArray native_testArray2(JNIEnv *env, jclass clazz, jint len) {
ALOGI("Call native_testArray2\n");
int *carr;
jintArray rarr;
carr = malloc(sizeof(int) * len);
for (int i = 0; i < len; i++) {
carr[i] = 10 + i;
}
/* create jintArray */
rarr = (*env)->NewIntArray(env, len);
(*env)->SetIntArrayRegion(env, rarr, 0, len, carr);
free(carr);
return rarr;
}
void native_cCallJava(JNIEnv *env, jobject thiz) {
ALOGI("Call native_cCallJava\n");
//jclass clazz = env->FindClass("com/example/jni_test/MainActivity");
jclass clazz = (*env)->GetObjectClass(env, thiz);
jmethodID methodId = (*env)->GetMethodID(env, clazz, "showDialog", "(Ljava/lang/String;)V");
(*env)->CallVoidMethod(env, thiz, methodId, (*env)->NewStringUTF(env, "Message from C"));
}
/* Added by yinsj
typedef struct {
char *name;
char *signature;
void *fnPtr;
} JNINativeMethod;
*/
static JNINativeMethod gMethods[] = {
{"native_testString", "(Ljava/lang/String;)Ljava/lang/String;",(void*)native_testString},
{"native_testArray", "([I)[I", (void*)native_testArray1},
{"native_testArray", "(I)[I", (void*)native_testArray2},
{"native_cCallJava", "()V", (void*)native_cCallJava},
};
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
jint result = -1;
//1. 首先, 從java虛擬機中獲得jni環境, 後面要使用env中提供的大量的函數
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("ERROR: GetEnv failed\n");
goto fail;
}
assert(env != NULL);
jclass clazz;
static const char* const kClassName="com/example/jni_test/MainActivity";
//2. 其次,指定要將這裏實現的本地方法映射到哪一個類中
clazz = (*env)->FindClass(env, kClassName);
if(clazz == NULL) {
ALOGE("Can not get class:%s\n", kClassName);
return -1;
}
//3. 最後, 將這裏實現的本地方法映射到上面指定的java類中
if((*env)->RegisterNatives(env, clazz,gMethods, sizeof(gMethods)/sizeof(gMethods[0])) != JNI_OK) {
ALOGE("RegisterNatives failed!\n");
return -1;
}
result = JNI_VERSION_1_4;
fail:
return result;
}
(3) Android.mk
#Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
# This is the target being built.
LOCAL_MODULE:= libnative
# All of the source files that we will compile.
LOCAL_SRC_FILES:= \
native.c
# All of the shared libraries we link against.
LOCAL_SHARED_LIBRARIES := \
libcutils
# No static libraries.
LOCAL_STATIC_LIBRARIES :=
# Need headers.
LOCAL_C_INCLUDES += \
../prebuilts/ndk/current/platforms/android-19/arch-arm/usr/include
# compiler flags.
LOCAL_CFLAGS += -std=c99 -w
include $(BUILD_SHARED_LIBRARY)
#include $(BUILD_EXECUTABLE)
(4) 編譯執行:
在上面結果的顯示過程中, 我們自己設置了一個過濾器, 只顯示Log Tag爲NATIVE和System.out的log,如下:
libcutils