Android JNI开发从0到1,java调C,C调Java,保姆级教程详解

简介: Android JNI开发从0到1,java调C,C调Java,保姆级教程详解

第一步首先配置Android studio的NDK开发环境,首先在Android studio中下载NDK包

第二步在local.properties文件中,配置对应的NDK路径

第三歩,在app目录下的build.gradle文件中的,android{}闭包中,指定CMakeLists.txt路径

// 在android节点下
    // 指定CMakeLists.txt路径
    externalNativeBuild {
        cmake {
            // 在该文件种设置所要编写的c源码位置,以及编译后so文件的名字
            path 'CMakeLists.txt'
        }
    }

在defaultConfig闭包下配置

// 增加cmake控制属性
        externalNativeBuild {
            cmake {
                // 指定编译架构
                abiFilters 'arm64-v8a', 'armeabi-v7a', 'x86', 'x86_64'
            }
        }

在app目录下,指定CmakeLists.txt文件,根据注释进行相应添加

# CMakeLists.txt
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
#CMakeLists.txt
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
add_library(
      # 设置so文件名称.
       Hello
       # 设置这个so文件为共享.
       SHARED
       # Provides a relative path to your source file(s).
       src/main/jni/Hello.c)
add_library(
        # 设置so文件名称.
        Test
        # 设置这个so文件为共享.
        SHARED
        # Provides a relative path to your source file(s).
        src/main/jni/Test.c)
add_library(
        # 设置so文件名称.
        CCallJava
        # 设置这个so文件为共享.
        SHARED
        # Provides a relative path to your source file(s).
        src/main/jni/CCallJava.c)
# 添加 log 库的链接
target_link_libraries(CCallJava log)
target_link_libraries(Test log)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
       log-lib
       # Specifies the name of the NDK library that
       # you want CMake to locate.
       log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
            # 制定目标库.
            Hello
            # Links the target library to the log library
            # included in the NDK.
            ${log-lib} )

第四步,首先进行Java代码C代码的操作

1.JNI2.java代码如下

/**
 * @Author: ly
 * @Date: 2023/8/6
 * @Description: java调C代码
 */
public class JNI2 {
    static {
        System.loadLibrary("Test"); //加载动态链接库
    }
    /**
     * 让C代码做加法运算,把结果返回
     * 场景:大量运算,编解码之类的,需要性能很高的情况下,可以用C代码
     *
     * @param x
     * @param y
     * @return
     */
    public native int add(int x, int y);
    /**
     * 从java传入字符串,C代码进行拼接
     *
     * @param s I am from java
     * @return I am from java and I am from c
     */
    public native String sayHello(String s);
    /**
     * 让C代码给每个元素都加上10
     *
     * @param intArray
     * @return
     */
    public native int[] increaseArrayEles(int[] intArray);
    /**
     * 应用:检查密码是否正确,如果正确返回200,否则返回400
     *
     * @param pwd
     * @return
     */
    public native int checkPwd(String pwd);
}

2.根据命令行javah +JNI2的全类名,生成Test.c对应的C代码头文件com_example_jniproject_JNI2.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jniproject_JNI2 */
#ifndef _Included_com_example_jniproject_JNI2
#define _Included_com_example_jniproject_JNI2
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jniproject_JNI2
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_jniproject_JNI2_add
  (JNIEnv *, jobject, jint, jint);
/*
 * Class:     com_example_jniproject_JNI2
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_jniproject_JNI2_sayHello
  (JNIEnv *, jobject, jstring);
/*
 * Class:     com_example_jniproject_JNI2
 * Method:    increaseArrayEles
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_com_example_jniproject_JNI2_increaseArrayEles
  (JNIEnv *, jobject, jintArray);
/*
 * Class:     com_example_jniproject_JNI2
 * Method:    checkPwd
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_example_jniproject_JNI2_checkPwd
  (JNIEnv *, jobject, jstring);
#ifdef __cplusplus
}
#endif
#endif

3.根据jni协议,去写对应的c代码,创建Test.c文件,代码如下

//
// Created by DELL on 2023/8/6.
//
#include "com_example_jniproject_JNI2.h"
#include "string.h"
#include <stdlib.h>
#include <android/log.h>
#define TAG "luyu"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
JNIEXPORT char *JNICALL Java_com_example_MyClass_myMethod(JNIEnv *env, jobject obj, jstring jstr) {
    const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
    // 使用 cstr 进行操作,并获取结果(假设结果为 result)
    (*env)->ReleaseStringUTFChars(env, jstr, cstr);
    char *result = (char *) malloc(strlen(cstr) + 1);
    strcpy(result, cstr);
    return result;
}
/**
 * jint:返回值
 * Java_全类名_方法名
 * JNIEnv *env
 */
jint Java_com_example_jniproject_JNI2_add
        (JNIEnv *env, jobject jobj, jint ji, jint jj) {
    int result = ji + jj;
    return result;
};
/**
     * 从java传入字符串,C代码进行拼接
     *
     * @param s I am from java
     * @return I am from java and I am from c
*/
jstring Java_com_example_jniproject_JNI2_sayHello
        (JNIEnv *env, jobject jobj, jstring jstring1) {
    char *fromJava = Java_com_example_MyClass_myMethod(env, jobj, jstring1);
    char *fromC = " and I am from C";
    //拼接函数,拼接后得到的结果放到第一个参数里面
    strcat(fromJava, fromC);//把拼接的结果放在第一个参数里面
    //jstring     (*NewStringUTF)(JNIEnv*, const char*);
    LOGE("fromJava==%s\n", fromJava);
    return (*env)->NewStringUTF(env, fromJava);
};
/**
 * 给每个元素加10
 * 场景:图片处理,颜色矩阵(就是数组),进行数组的处理
 */
jintArray Java_com_example_jniproject_JNI2_increaseArrayEles
        (JNIEnv *env, jobject jobj, jintArray jintArray1) {
    //1.得到数组的长度
    //jsize       (*GetArrayLength)(JNIEnv*, jarray);
    jsize size = (*env)->GetArrayLength(env, jintArray1);
    //2.得到数组的元素
    // jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
    jint *intArray = (*env)->GetIntArrayElements(env, jintArray1,
                                                 JNI_FALSE);//这里传0 false表示 在同一份内存操作,不开辟新的内存
    //3.遍历数组,给每个元素加10
    int i;
    for (i = 0; i < size; i++) {
        *(intArray + i) += 10;
    }
    // 4. 同步修改到 Java 层
    (*env)->ReleaseIntArrayElements(env, jintArray1, intArray, 0);
    //4.返回结果
    return jintArray1;
};
/**
 * 应用:检查密码是否正确,如果正确返回200,否则返回400
 */
jint Java_com_example_jniproject_JNI2_checkPwd
        (JNIEnv *env, jobject jobj, jstring jstring1) {
    //假设服务器的密码是123456
    char *origin = "123456";
    char *fromUser = Java_com_example_MyClass_myMethod(env, jobj, jstring1);
    //函数比较字符串是否相同
    int code = strcmp(origin, fromUser);
    LOGE("code==%d\n", code);
    if (code == 0) {
        return 200;
    } else {
        return 400;
    }
};

第五步,C代码调用Java代码,首先创建JNI3.java

/**
 * @Author: ly
 * @Date: 2023/8/6
 * @Description: C代码调用Java代码
 */
public class JNI3 {
    static {
        System.loadLibrary("CCallJava"); //加载动态链接库
    }
    //当执行这个方法的时候,让C代码调用
    //public int add(int x, int y)
    public native void callbackAdd();
    /**
     * 当执行这个方法的时候,让C代码调用
     * public void helloFromJava()
     */
    public native void callbackHelloFromJava();
    /**
     * 当执行这个方法的时候,让C代码调用
     * public void printString(String s)
     */
    public native void callbackPrintString();
    /**
     * 当执行这个方法的时候,让C代码调用
     * public static void sayHello(String s)
     */
    public native void callbackSayHello();
    public int add(int x, int y) {
        Log.e("TAG", "add()  x=" + x + " y=" + y);
        return x + y;
    }
    public void helloFromJava() {
        Log.e("TAG", "helloFromJava");
    }
    public void printString(String s) {
        Log.e("TAG", "C中输入的:" + s);
    }
    public static void sayHello(String s) {
        Log.e("TAG", "我是java代码中的JNI" +
                ".java中的sayHello(String s)静态方法,我被C调用了:" + s);
    }
}

2.根据命令行,javah +JNI3全类名,生成CCallJava.c对应的头文件

com_example_jniproject_JNI3.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jniproject_JNI3 */
#ifndef _Included_com_example_jniproject_JNI3
#define _Included_com_example_jniproject_JNI3
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_jniproject_JNI3
 * Method:    callbackAdd
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackAdd
  (JNIEnv *, jobject);
/*
 * Class:     com_example_jniproject_JNI3
 * Method:    callbackHelloFromJava
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackHelloFromJava
  (JNIEnv *, jobject);
/*
 * Class:     com_example_jniproject_JNI3
 * Method:    callbackPrintString
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackPrintString
  (JNIEnv *, jobject);
/*
 * Class:     com_example_jniproject_JNI3
 * Method:    callbackSayHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackSayHello
  (JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif

3.对应的CCallJava.c文件中的代码如下

//
// Created by DELL on 2023/8/6.
//
#include "com_example_jniproject_JNI3.h"
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>
#define TAG "luyu"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
/**
 * 让C代码调用Java中JNI类的public int add(int x ,int y)
 */
JNIEXPORT void Java_com_example_jniproject_JNI3_callbackAdd(JNIEnv *env, jobject jobj) {
    //1.得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3");
    //2.得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //最后一个参数是方法签名
    jmethodID jmethodId = (*env)->GetMethodID(env, jclazz, "add", "(II)I");
    //3.实例化该类
    jobject jobject1 = (*env)->AllocObject(env, jclazz);
    //4.调用方法
    //jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    jint value = (*env)->CallIntMethod(env, jobject1, jmethodId, 99, 1);
    //成功调用
    LOGE("value==%d\n", value);
};
/***
 * 让C代码调用
 * public void helloFromJava()
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackHelloFromJava
        (JNIEnv *env, jobject jobj) {
    //1.得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3");
    //2.得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //最后一个参数是方法签名
    jmethodID jmethodIds = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V");
    //3.实例化该类
    jobject jobject1 = (*env)->AllocObject(env, jclazz);
    //4.调用方法
    //jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env, jobject1, jmethodIds);
};
/***
 * 让C代码调用
 * public void printString(String s)
 *
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackPrintString
        (JNIEnv *env, jobject jobj) {
    //1.得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3");
    //2.得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //最后一个参数是方法签名
    jmethodID jmethodIds = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V");
    //3.实例化该类
    jobject jobject1 = (*env)->AllocObject(env, jclazz);
    //4.调用方法
    //jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    // jstring     (*NewStringUTF)(JNIEnv*, const char*);
    jstring jst = (**env).NewStringUTF(env, "I am Android!");
    (*env)->CallVoidMethod(env, jobject1, jmethodIds, jst);
};
/**
 * 让C代码调用
 * public static void sayHello(String s)
 *
 */
JNIEXPORT void JNICALL Java_com_example_jniproject_JNI3_callbackSayHello
        (JNIEnv *env, jobject jobj){
    //1.得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env, "com/example/jniproject/JNI3");
    //2.得到方法
    // jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
    //最后一个参数是方法签名
    jmethodID jmethodIds = (*env)->GetStaticMethodID(env, jclazz, "sayHello", "(Ljava/lang/String;)V");
    jstring jst = (**env).NewStringUTF(env, "I am Android!");
    (*env)->CallStaticVoidMethod(env,jclazz,jmethodIds,jst);
};

之后,在MainActivity中调用对应的方法即可

public class MainActivity extends AppCompatActivity {
    private JNI2 jin2;
    private static final String TAG = "MainActivity";
    private JNI3 mJNI3;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        jin2 = new JNI2();
        mJNI3 = new JNI3();
    }
    /******以下是Java代码调用C*****/
    public void checkPwd(View view) {
        int result = jin2.checkPwd("12345678");
        Log.i(TAG, "result==" + result);
    }
    public void increaseArrayEles(View view) {
        int array[] = {1, 2, 3, 4};
        jin2.increaseArrayEles(array);
        for (int i = 0; i < array.length; i++) {
            Log.i(TAG, "result[" + i + "]:" + array[i]);
        }
    }
    public void sayHello(View view) {
        String result = jin2.sayHello("I am from java");
        Log.i(TAG, "result==" + result);
    }
    public void add(View view) {
        int result = jin2.add(99, 1);
        Log.i(TAG, "result==" + result);
    }
    /******以下是C调Java代码*****/
    public void callbackAdd(View view) {
        mJNI3.callbackAdd();
    }
    public void callbackHelloFromJava(View view) {
        mJNI3.callbackHelloFromJava();
    }
    public void callbackPrintString(View view) {
        mJNI3.callbackPrintString();
    }
    public void callbackSayHello(View view) {
        mJNI3.callbackSayHello();
    }
}

点击按钮便可输出对应的日志,表示调用成功。


目录
相关文章
|
11月前
|
Java 关系型数据库 数据库
Java 项目实战教程从基础到进阶实战案例分析详解
本文介绍了多个Java项目实战案例,涵盖企业级管理系统、电商平台、在线书店及新手小项目,结合Spring Boot、Spring Cloud、MyBatis等主流技术,通过实际应用场景帮助开发者掌握Java项目开发的核心技能,适合从基础到进阶的学习与实践。
1475 4
|
10月前
|
安全 Java
Java之泛型使用教程
Java之泛型使用教程
481 10
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
884 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
9月前
|
Oracle Java 关系型数据库
Java 简单教程
Java是跨平台、面向对象的编程语言,广泛用于企业开发、Android应用等。本教程涵盖环境搭建、基础语法、流程控制、面向对象、集合与异常处理,助你快速入门并编写简单程序,为进一步深入学习打下坚实基础。
609 0
|
12月前
|
缓存 安全 Java
Java 并发新特性实战教程之核心特性详解与项目实战
本教程深入解析Java 8至Java 19并发编程新特性,涵盖CompletableFuture异步编程、StampedLock读写锁、Flow API响应式流、VarHandle内存访问及结构化并发等核心技术。结合电商订单处理、缓存系统、实时数据流、高性能计数器与用户资料聚合等实战案例,帮助开发者高效构建高并发、低延迟、易维护的Java应用。适合中高级Java开发者提升并发编程能力。
590 0
|
Oracle Java 关系型数据库
java 编程基础入门级超级完整版教程详解
这份文档是针对Java编程入门学习者的超级完整版教程,涵盖了从环境搭建到实际项目应用的全方位内容。首先介绍了Java的基本概念与开发环境配置方法,随后深入讲解了基础语法、控制流程、面向对象编程的核心思想,并配以具体代码示例。接着探讨了常用类库与API的应用,如字符串操作、集合框架及文件处理等。最后通过一个学生成绩管理系统的实例,帮助读者将理论知识应用于实践。此外,还提供了进阶学习建议,引导学员逐步掌握更复杂的Java技术。适合初学者系统性学习Java编程。资源地址:[点击访问](https://pan.quark.cn/s/14fcf913bae6)。
1282 2
|
消息中间件 Java 数据库
自研Java框架 Sunrays-Framework使用教程「博客之星」
### Sunrays-Framework:助力高效开发的Java微服务框架 **Sunrays-Framework** 是一款基于 Spring Boot 构建的高效微服务开发框架,深度融合了 Spring Cloud 生态中的核心技术组件。它旨在简化数据访问、缓存管理、消息队列、文件存储等常见开发任务,帮助开发者快速构建高质量的企业级应用。 #### 核心功能 - **MyBatis-Plus**:简化数据访问层开发,提供强大的 CRUD 操作和分页功能。 - **Redis**:实现高性能缓存和分布式锁,提升系统响应速度。 - **RabbitMQ**:可靠的消息队列支持,适用于异步
自研Java框架 Sunrays-Framework使用教程「博客之星」
|
移动开发 前端开发 Java
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
JavaFX是Java的下一代图形用户界面工具包。JavaFX是一组图形和媒体API,我们可以用它们来创建和部署富客户端应用程序。 JavaFX允许开发人员快速构建丰富的跨平台应用程序,允许开发人员在单个编程接口中组合图形,动画和UI控件。本文详细介绍了JavaFx的常见用法,相信读完本教程你一定有所收获!
15833 5
Java最新图形化界面开发技术——JavaFx教程(含UI控件用法介绍、属性绑定、事件监听、FXML)
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
1001 26
|
Java 数据库连接 数据处理
探究Java异常处理【保姆级教程】
Java 异常处理是确保程序稳健运行的关键机制。它通过捕获和处理运行时错误,避免程序崩溃。Java 的异常体系以 `Throwable` 为基础,分为 `Error` 和 `Exception`。前者表示严重错误,后者可细分为受检和非受检异常。常见的异常处理方式包括 `try-catch-finally`、`throws` 和 `throw` 关键字。此外,还可以自定义异常类以满足特定需求。最佳实践包括捕获具体异常、合理使用 `finally` 块和谨慎抛出异常。掌握这些技巧能显著提升程序的健壮性和可靠性。
395 4