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_H
int 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函数调用了