开发环境
名称 | 版本 |
操作系统 | window10 |
Android Studio | Koala // 2024.1.1 |
SDK | 34 |
NDK | 28.0.12433566 rc1 |
JDK | 23 |
Build configuration language | kotlin DSL(build.gradle.kt) |
示例
一、新建APK工程
此处选择的是Empty view Activity,语言选择java,Build configuration language选择kotlin(注意,java和Kotlin语言配置方式不一致,建议二者还是保持一致)。具体配置如后面两张图所示:
右键 com.example.jnitest - new - java class 新建一个myJNI文件,内容如下:
package com.example.jnitest;
public class myJNI {
//加载so库
static {
System.loadLibrary("JNItest");
}
//native方法
public static native String sayHello();
}
前提条件已准备完成,项目结构如下图:
二、使用JDK工具生成class和h文件
1.进入源码文件夹,使用 javac 指令在当前目录先后生成 .class 和 .h 文件如下所示:
指令:javac -encoding utf8 -h targetDir sourceFile
JDK下载路径:Java Downloads | Oracle 中国
PS C:\Users\huangjiayi\AndroidStudioProjects\MyJNI> cd .\app\src\main\java\com\example\myjni\
PS C:\Users\huangjiayi\AndroidStudioProjects\MyJNI\app\src\main\java\com\example\myjni> javac .\FirstJni.java
PS C:\Users\huangjiayi\AndroidStudioProjects\MyJNI\app\src\main\java\com\example\myjni> javac -encoding utf8 -h . .\FirstJni.java
生成的.h文件内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_myjni_FirstJni */
#ifndef _Included_com_example_myjni_FirstJni
#define _Included_com_example_myjni_FirstJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_myjni_FirstJni
* Method: sayHello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_myjni_FirstJni_sayHello
(JNIEnv *env, jclass);
#ifdef __cplusplus
}
#endif
#endif
2.右键 main - New - Folder - JNI Folder 新建 JNI 文件夹以方便管理 JNI 文件和java文件,把h和class文件移到 JNI 文件夹下.
3.在 JNI 文件夹下新建 hello.c 文件,把 .h 文件内容拷贝过来,并实现函数体,如下所示,在c文件内返回一个字符串 This output comes from JNI ...
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_myjni_FirstJni */
#ifndef _Included_com_example_myjni_FirstJni
#define _Included_com_example_myjni_FirstJni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_myjni_FirstJni
* Method: sayHello
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_example_myjni_FirstJni_sayHello
(JNIEnv *env, jclass){
return (*env)->NewStringUTF(env, " This output comes from JNI ...");
}
#ifdef __cplusplus
}
#endif
#endif
三、修改配置文件
1.修改 app 目录下的 build.gradle.kt 文件,在 Android 节点添加以下代码:
android {
...
externalNativeBuild {
cmake {
path ("CMakeLists.txt")
}
}
ndkVersion = "28.0.12433566 rc1"
}
2.同时在 app 文件夹下新建 CMakeLists.txt 文件,内容如下
cmake_minimum_required(VERSION 3.4.1)
add_library(
FirstJni
SHARED
src/main/jni/hello.c)
find_library(
log-lib
log)
target_link_libraries(
FirstJni
${log-lib})
四、生成so库
此时rebuild项目,会在以下目录下生成 so 文件
\JNItest2\app\build\intermediates\merged_native_libs\debug\mergeDebugNativeLibs\out\lib\armeabi-v7a\
到此为止,JNI的配置完成
五、使用JNI
1.在 MainActivity内新建一个button,添加onClick属性:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
.....
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="28dp"
android:text="Button"
android:onClick="onclock"
app:layout_constraintBottom_toTopOf="@+id/textView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
2.在 MainActivity 实现接口。
package com.example.myjni;
import ...
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
...
}
public void onclock(View view){
Toast.makeText(getApplicationContext(),FirstJni.sayHello(),Toast.LENGTH_SHORT).show();
}
}
到此,点击button 会打印弹窗 This output comes from JNI ...
---------------------------------------------------------分割线---------------------------------------------------------------
项目拓展至C调JAVA接口
1.在myJNI.java新增本地方法 helloFromJava()
package com.example.jnitest;
public class myJNI {
//加载so库
static {
System.loadLibrary("JNItest");
}
//加static转为静态方法,可以直接由class来使用,不必建立class的实例
//在JNI中使用jclass即可调用,此处取消static,jclass调用将会报错
public static void helloFromJava(){
System.out.println("printf form java");
}
//native方法 JNI方法 将由C/C++实现
public static native String sayHello();
}
2.使用上文指令重新生成class文件和h文件
javac .\FirstJni.java
javac -encoding utf8 -h . .\FirstJni.java
3.在原先JAVA掉C接口内,回调JAVA接口,即可在system log中看到打印 printf form java
JNIEXPORT jstring JNICALL Java_com_example_myjni_FirstJni_sayHello
(JNIEnv *env, jclass FirstJni){
/*
* GetStaticMethodID: 回调Java接口
* First param:env java环境变量
* second param:myJNI 字节码文件对象,来自形参jclass
* third param:helloFromJava 调用的java方法名
* fourth param:()V 内部类型签名,括号里面是方法参数,V是返回值(Void)
*/
jmethodID id = (*env)->GetStaticMethodID(env, FirstJni, "helloFromJava", "()V");
(*env)->CallStaticVoidMethod(env,FirstJni,id);
return (*env)->NewStringUTF(env, " This output comes from JNI ...");
}