Android Studio 使用JNI (java 调 C) 基础示例

开发环境

名称版本
操作系统window10
Android StudioKoala // 2024.1.1
SDK34
NDK28.0.12433566 rc1        
JDK23
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 ...");
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值