android应用调用c语言的so库,可以将c代码放在android工程里直接ndk编译,或者外部一个so库,ndk编一个调用so的桥接库。普通的c函数java代码不能直接调用,桥接的虚函数不可少,所以此处仅记录一下桥接函数。有的固定路径的项目,会把桥接函数也固化在so里。
第一步在build.gradle里增加ndk编译脚本
externalNativeBuild {ndkBuild {path file("src/main/jni/Android.mk")}}
第2步,编辑脚本
#表示Android.mk所在目录
LOCAL_PATH := $(call my-dir)
#CLEAR_VARS变量指向特殊 GNU Makefile,用于清除部分LOCAL_变量
include $(CLEAR_VARS)
APP_ABI := arm64-v8a#LOCAL_SHARED_LIBRARIES := ../jniLibs/arm64-v8a/libfwupdate
LOCAL_LDLIBS := -L/home/qiaozc/StudioProjects/emv/app/src/main/jniLibs/arm64-v8a -lfwupdate
# 添加对日志库的引用
LOCAL_LDLIBS += -llog#模块名称
LOCAL_MODULE := detectemv
#构建系统用于生成模块的源文件列表
LOCAL_SRC_FILES := detectemv.c#BUILD_SHARED_LIBRARY 表示.so动态库
#BUILD_STATIC_LIBRARY 表示.a静态库
include $(BUILD_SHARED_LIBRARY)
第3步,编写桥接c文件
#include <android/log.h>
#include <jni.h>
#include <string.h>
#include "fwupdate.h"#define LOG_TAG "DEMO"#define LOGD(fmt, ...) \__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, fmt, ##__VA_ARGS__)JNIEXPORT jint JNICALL Java_com_test_nativelib_FwUpdateLib_detectEmv(JNIEnv *env, jobject thiz, jbyteArray jOutCard, jbyteArray jOutDate) {LOGD("In card detected callback");char cardNumber[64] = {0};char expireDate[8] = {0};int RetVal = detectEmv(cardNumber, expireDate);LOGD("RetVal=%s date=%s ", cardNumber, expireDate);int len = strlen(cardNumber);// 把elems复制给arr,从第0位开始,长度位len(*env)->SetByteArrayRegion(env, jOutCard, 0, len, cardNumber);len = strlen(expireDate);(*env)->SetByteArrayRegion(env, jOutDate, 0, len, expireDate);
}
第4步,我这里桥接就是将原so库的函数在这里调用将值再传出去,引用了需要的函数声明的头文件,自己可以造一个fwupdate.h
#ifndef VALIDATOR_FWUPDATE_H
#define VALIDATOR_FWUPDATE_Hint detectEmv(char *cardNumber, char *expireDate);#endif //VALIDATOR_FWUPDATE_H
第5步,将桥接函数再映射到java里
package com.test.nativelib;public class FwUpdateLib {static {System.loadLibrary("detectemv");}native public int detectEmv(byte[] cardNumber, byte[] expireDate);}
后面在java里就可以正常当java函数调用了