1 参考
2 JNI与JNA
2.1 JNI
JNI
(Java Native Interface,Java
本地接口)是一种编程框架,使得Java虚拟机中的Java程序可以调用本地应用/库,也可以被其他程序调用。 本地应用/库一般是用其它语言(C
、C++
或汇编语言等)编写的,并且被编译为基于本机硬件和操作系统的程序/库。
JNI执行过程:
整个过程,如果全部是分开,一步一步分开手动实现就比较复杂,但是Android Studio
和Android SDK
已经为安卓开发者提供了强大的NDK工具包,使得IDEA自动为我们做了很多步骤,比如快速生成头文件,可以直接在Android Studio
上写c/c++
代码,交叉编译出多种cpu
平台的的链接库文件,然后打包进apk
,集成链接库文件到安卓项目。
Android Studio项目开发带JNI的项目配置过程可以参考NDK开发笔记之1:AndroidStudio3.3配置Ndk和集成OpenCV4.0一个简单例子
优点:
- JNI可以实现java调c/c++的函数,也能实现c/c++调java层的方法
缺点:
- 使用过程步骤多,较复杂
2.2 JNA
JNA(Java Native Access)
是建立在JNI
技术基础之上的Java
类库,它可以使我们很方便的使用Java
直接访问动态链接库中的函数。
JNA执行过程:
优点:
- JNA相对于JNI只需要我们写Java代码而不用写JNI那层接口代码。
缺点:
- JNA不能完全替代JNI,JNA只能实现Java访问C函数,作为一个Java框架,自然不能实现C语言调用Java代码
2.2.1 AndroidStudio3.3+Cmake+JNA例子
目的:快速实现java层调用c的add()函数。
1 下载JNA相关库文件
我们可以把jna项目克隆下来。然后进入里面的dist文件夹。
2 找到jna-min.jar
,导入AS
项目
3 找到相关安卓平台的libjnidispatch.so
文件,导入到AS
项目
比如解压android-aarch64.jar,即可找到arm64-v8a的so文件。其它平台也是一样的获取方法。
主要在app的build.gradle中添加下处理META-INF错误的配置
android {
...
//处理所有报META-INF/*'的错误
packagingOptions {
pickFirst 'META-INF/*'
}
...
}
4 配置AS
项目c
源码位置及编译
新建c源码文件夹及.c/.h文件,这里我只写了一个简单的c函数:
hello.h
#ifndef JNADEMO_HELLO_H
#define JNADEMO_HELLO_H
int add (int x,int y);
#endif //JNADEMO_HELLO_H
hello.c
#include "hello.h"
/**
* int
*/
int add (int x,int y){
return x+y;
}
5 新建本地库编译配置文件CMakeLists.txt
我一般是放在app模块的根目录:
CMakeLists.txt
# 设置Cmake的最低版本号
cmake_minimum_required(VERSION 3.6.0)
#############################################################引入头文件开始###############################################################################
# 引入opencv 头文件
set(headers ${CMAKE_SOURCE_DIR}/src/main/cpp/include)
include_directories(${headers})
#############################################################引入头文件结束###############################################################################
#############################################################引入链接库开始###############################################################################
#############################################################引入链接库结束###############################################################################
# 定义自己写的总库
add_library(hello
SHARED
${CMAKE_SOURCE_DIR}/src/main/cpp/hello.c
)
# 如果需要其他NDK原生库的话,可自行在此处添加
find_library(log-lib
log)
# 将第三方库以及自己写的库关联到需要用的NDK原生库中
target_link_libraries(
hello
${log-lib})
6 app模块的build.gradle添加一些Cmake的配置
cmake编译配置:
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"
arguments "-DANDROID_STL=c++_shared"
//abiFilters 'armeabi-v7a','arm64-v8a'
}
}
...
}
...
}
c源码位置和依赖的链接库文件位置
android {
...
sourceSets {
main {
jni.srcDirs = ["src/main/cpp"] //指定c/c++源码位置
jniLibs.srcDirs = ['src/main/jniLibs']
}
}
...
}
CMakeLists.txt文件位置
android {
...
externalNativeBuild {
cmake {
path file('CMakeLists.txt')
}
...
}
7 java层调用
定义c层函数对应的Java接口:
/**
* @author newtrekWang
* @fileName Clibrary
* @createDate 2019/6/5 10:36
* @email 408030208@qq.com
* @desc 底层库函数
*/
public interface Clibrary extends Library {
// 加载libhello.so文件
Clibrary INSTANCE = Native.loadLibrary("hello",Clibrary.class);
// 与c层的add函数相对应
int add(int x,int y);
}
应用层调用函数:
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int result = Clibrary.INSTANCE.add(1,2);
Log.d(TAG, "onClick: >>>getInt: "+result);
Toast.makeText(MainActivity.this, "getInt:"+result, Toast.LENGTH_SHORT).show();
}
});
}
}
效果:
可见应用已成功调用add()
函数,输出结果。