- 简介
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