Android --- 搭建JNI框架
JNI(Java Native Interface,Java 本地接口)是 Java 平台提供的一种编程框架,用于实现 Java 代码与其他编程语言(主要是 C/C++)之间的交互。它的核心作用是打破 Java 的跨平台特性限制,让 Java 程序能够调用本地代码(如操作系统底层 API、硬件驱动、高性能计算库等),同时也允许本地代码调用 Java 方法。
在android平台搭建JNI框架:
Step1. 明确jni要封装的其他编程语言(主要是 C/C++)的函数并提供给java调用的接口
为了方便举例,在C++库中定义了一个加法函数
此代码库在后续的步骤中会使用mk编译成.so库
sum_test.h
#ifndef SUM_TEST_H
#define SUM_TEST_H#include <cstdint>uint32_t additionTest(uint32_t a, uint32_t b);#endif // SUM_TEST_H
sum_test.cpp
#include "sum_test.h"
#include <android/log.h>#define LOG_TAG "LibSum"uint32_t additionTest(uint32_t a, uint32_t b) {__android_log_print(ANDROID_LOG_DEBUG, "yuan", "additionTest() start!! (I'm from libsum)");return a + b;
}
Step2. 编写Java类:声明native方法
Java 层声明本地方法:在 Java 类中用native关键字声明需要调用的本地方法(无方法体)
注意要使用静态代码块加载含本地方法的动态链接库(要调用的C/C++库的名字去掉lib)
AppInstance.java
package com.app.test;public class AppInstance {static {System.loadLibrary("sum_jni");}public native int additionTest(int a, int b);}
Step3. 生成JNI头文件:使用javac和javah命令
3.1 用source和lunch加载一下环境变量
source build/envsetup.sh
lunch 编译目标数字编号
3.2 使用javac命令生成头文件
prebuilts/jdk/jdk9/linux-x86/bin/javac -h ./vendor/xxx/packages/apps/AppTest/jni/ ./vendor/xxx/packages/apps/AppTest/src/com/app/test/AppInstance.java
命令解读:
① prebuilts/jdk/jdk9/linux-x86/bin/javac 在代码的以下路径有javac编译器工具
② ./vendor/xxx/packages/apps/AppTest/jni/ jni头文件的生成路径
③ ./vendor/xxx/packages/apps/AppTest/src/com/app/test/AppInstance.java 要编译的java文件
执行此命令会在/vendor/xxx/packages/apps/AppTest/jni路径下自动生成com_app_test_AppInstance.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_app_test_AppInstance */#ifndef _Included_com_app_test_AppInstance
#define _Included_com_app_test_AppInstance
#ifdef __cplusplus
extern "C" {
#endif
/** Class: com_app_test_AppInstance* Method: additionTest* Signature: (II)I*/
JNIEXPORT jint JNICALL Java_com_app_test_AppInstance_additionTest(JNIEnv *, jobject, jint, jint);#ifdef __cplusplus
}
#endif
#endif
Step4. 根据头文件实现jni代码(可以用C/C++编写)
com_app_test_AppInstance.cpp
#include "com_app_test_AppInstance.h"
#include "../libsum/sum_test.h"
#include <android/log.h>extern "C" {JNIEXPORT jint JNICALL Java_com_app_test_AppInstance_additionTest(JNIEnv* env, jobject obj, jint a, jint b) {__android_log_print(ANDROID_LOG_DEBUG, "yuan", "Java_com_app_test_AppInstance_additionTest() start!! ""(I'm from libsum_jni)");return additionTest(a, b);}
}
① 注意要引用.so库中要调用的函数所在的头文件
② 函数是根据从java中传入的参数并调用.so库中的函数
Step5. 编写Android.mk将java层、jni层、C/C++库的代码编译到android平台
① .so库层
编译出.so库,分别将64位和32位的.so push到车机的system/lib64和system/lib路径下
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE := libsumLOCAL_SRC_FILES := sum_test.cppLOCAL_SHARED_LIBRARIES := libloginclude $(BUILD_SHARED_LIBRARY)
② jni层
编译出.so库,分别将64位和32位的.so push到车机的system/lib64和system/lib路径下
注意要添加对①的依赖:LOCAL_SHARED_LIBRARIES := libsum
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE := libsum_jniLOCAL_SRC_FILES := \com_app_test_AppInstance.cppLOCAL_C_INCLUDES := \$(JNI_H_INCLUDE)LOCAL_SHARED_LIBRARIES := \libbase \liblog \libsum \include $(BUILD_SHARED_LIBRARY)
③ app层
编译出apk,在system/app下创建一个自己app名字的文件并push到这里(需要安装成系统app,否则无法访问system分区下的.so库)
注意要添加对②的依赖:LOCAL_JNI_SHARED_LIBRARIES := libsum_jni
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src)LOCAL_PACKAGE_NAME := TestAppLOCAL_PRIVATE_PLATFORM_APIS := trueLOCAL_CERTIFICATE := platformLOCAL_PRIVILEGED_MODULE := falseLOCAL_USE_AAPT2 := trueLOCAL_PROGUARD_ENABLED := disabledLOCAL_DEX_PREOPT := falseLOCAL_RESOURCE_DIR := $(LOCAL_PATH)/resLOCAL_JNI_SHARED_LIBRARIES := libsum_jniLOCAL_STATIC_ANDROID_LIBRARIES:= androidx.appcompat_appcompat \androidx-constraintlayout_constraintlayoutinclude $(BUILD_PACKAGE)include $(call all-makefiles-under, $(LOCAL_PATH))
Step6. 在app内调用native方法
通过AppInstance实例调用native方法additionTest()
MainActivity.java
public class MainActivity extends Activity {private AppInstance appInstance = new AppInstance();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Log.d("yuan", "onCreate() start!! (I'm from app)");int result = appInstance.additionTest(10, 20);Log.d("yuan", "result:" + result);}}
最终通过log输出来查看代码的调用流程:
$ logcat | grep yuan
05-20 02:27:14.883 3336 3336 D yuan : onCreate() start!! (I'm from app)
05-20 02:27:14.884 3336 3336 D yuan : Java_com_app_test_AppInstance_additionTest() start!! (I'm from libsum_jni)
05-20 02:27:14.884 3336 3336 D yuan : additionTest() start!! (I'm from libsum)
05-20 02:27:14.884 3336 3336 D yuan : result:30
① app调用native方法
② 调用jni层函数
③ 调用.so库层函数
④ app通过jni的接口调用.so库成功~(返回结果30=10 + 20)