JNI :java native interface java本地接口,native一般指c/c++;
废话不多说,首先说明一下这篇博客总体大纲
1.生成.h头文件
2. 书写c/c++代码
3. 生成.so库
4. 实现java和c/c++互相通信
1.打开AS ,创建一个新的项目,记得要勾上 Include c++ support.(勾上后,生成的项目会有写好的jni调用实例)
创建完项目后,我们新建一个JniTest Java类, 代码如下:
public class JniTest {
public native String getStrFromC();
}
getStrFromC函数需要用native修饰,表明是一个和本地通信函数.
然后开始使用 javah命令生成c/c++类,
接着点击小锤子图标,生成.class文件
.class文件可以在…\app\build\intermediates\classes\debug\com……\jnitest
路径下找到,
下一步我们需要用这个.class文件生成.h文件
点击AS下方的Terminal, 进入到main目录下输入指令
javah -d jni -jni -classpath
D:\as\JNITest\app\build\intermediates\classes\debugcom.sakura.administrator.jnitest.JniTest
这个命令会在main目录下生成一个Jni目录,并在jni目录下创建一个头文件
分析一下这个 javah 命令
javah -d jni -jni -classpath 在这个目录下生成一个jni目录,在这目录下生成一个头文件,后边跟.class文件
在-classpath 后添加:
......\app\build\intermediates\classes\debug
到debug就可以,
然后在debug后边接上包名,接上Jni 类名 ,就组成这个命令,注意格式!!!
到此,第一步生成.h头文件文件完成.
2.
有了.h文件,我们就接着书写c++代码.
在刚生成的jni目录下新建 c++类
注意: 命名要和.h文件名一致
jni里的函数是按”Java_完整的包名activity类名函数名”来命名的
extern “C” 语句是用于兼容C动态库使用的, 不加会发生执行出错.
c++代码如下:
#include <string>
#include "com_sakura_administrator_jnitest_JniTest.h"
jstring Java_com_sakura_administrator_jnitest_JniTest_getStrFromC
(JNIEnv *env, jobject){
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
注意要用#include 预编译指令编译刚生成的.h头文件
代码只是简单的测试代码
书写c/c++代码结束
3.
c/c++代码写完之后,开始生成.so库文件
生成so文件之前,需要先写Android.mk和Application.mk两个配置文件,并在build.gradle中进行其他配置
(1).在之前生成的jni目录下新建一个Android.mk文件,内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JNI
LOCAL_SRC_FILES := com_sakura_administrator_jnitest_JniTest.cpp
include $(BUILD_SHARED_LIBRARY)
lOCAL_MODULE 是生成的.so库文件名称
LOCAL_SRC_FILES 是你写的c++文件
(2).
在之前生成的jni目录下新建一个Application.mk文件,内容如下:
APP_ABI := all
APP_STL := stlport_static
APP_ABI := all 生成适合所有架构的.so库文件
APP_STL := stlport_static 是支持STL运行库,(不写这句话,在c++代码中使用string,使用ndk编译时会报错.)
(3).
配置build.gradle
打开build.gradle,在defaultConfig括号里添加
ndk{
moduleName "JNI"
}
然后在android括号下添加
sourceSets {
main() {
jni.srcDirs = ['src/main/jni', 'src/main/jni/']
jniLibs.srcDirs = ['libs']
}
}
(4).
打开gradle.properties文件
添加
android.useDeprecatedNdk=true
配置完成,然后打开Terminal终端,
进入到刚生成的jni目录的父目录下
输入ndk-build 指令
然后适合各个架构的.so库文件就自动生成了
4.
java调c++方法
把.so库文件放到libs目录下,
修改jniTest类;
public class JniTest {
static{
System.loadLibrary("JNI");
}
public native String getStrFromC();
}
动态加载so库(生成的so库文件名字是libJNI.so)
在MainActivity类:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.sample_text);
String str=new JniTest().getStrFromC();
tv.setText(str);
}
}
展示从c++返回的字符串
所有流程结束,我们来看下运行效果:
完美!
PS:
javah 命令,看具体需求
jni调用繁琐,但胜在能调用java不具备的特性
gradle 配置也不是很懂…有什么不对的地方请大家指正,多谢!