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();
    }
}

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


目录
相关文章
|
4天前
|
Android开发
Android JNI与CAN通信遇到的问题总结
Android JNI与CAN通信遇到的问题总结
26 1
|
5天前
|
Android开发
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
Android JNI 报错(signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr )
27 1
|
1天前
|
Java 编译器 API
Java基础教程(17)-Java8中的lambda表达式和Stream、Optional
【4月更文挑战第17天】Lambda表达式是Java 8引入的函数式编程特性,允许函数作为参数或返回值。它有简洁的语法:`(parameters) -> expression 或 (parameters) ->{ statements; }`。FunctionalInterface注解用于标记单方法接口,可以用Lambda替换。
|
2天前
|
数据采集 前端开发 测试技术
《手把手教你》系列技巧篇(三十一)-java+ selenium自动化测试- Actions的相关操作-番外篇(详解教程)
【4月更文挑战第23天】本文介绍了网页中的滑动验证码的实现原理和自动化测试方法。作者首先提到了网站的反爬虫机制,并表示在本地创建一个没有该机制的网页,然后使用谷歌浏览器进行验证。接着,文章详细讲解了如何使用WebElement的click()方法以及Action类提供的API来模拟鼠标的各种操作,如右击、双击、悬停和拖动。
6 2
|
3天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。
|
3天前
|
设计模式 存储 前端开发
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
Java从入门到精通:2.2.1学习Java Web开发,了解Servlet和JSP技术,掌握MVC设计模式
|
3天前
|
Web App开发 数据采集 Java
《手把手教你》系列技巧篇(三十)-java+ selenium自动化测试- Actions的相关操作下篇(详解教程)
【4月更文挑战第22天】本文介绍了在测试过程中可能会用到的两个功能:Actions类中的拖拽操作和划取字段操作。拖拽操作包括基本讲解、项目实战、代码设计和参考代码,涉及到鼠标按住元素并将其拖动到另一个元素上或指定位置。划取字段操作则介绍了如何在一段文字中随机选取一部分,包括项目实战、代码设计和参考代码。此外,文章还提到了滑动验证的实现,并提供了相关的代码示例。
32 2
|
3天前
|
安全 Java
Java基础教程(15)-多线程基础
【4月更文挑战第15天】Java内置多线程支持,通过Thread类或Runnable接口实现。线程状态包括New、Runnable、Blocked、Waiting、Timed Waiting和Terminated。启动线程调用start(),中断线程用interrupt(),同步用synchronized关键字。线程安全包如java.util.concurrent提供并发集合和原子操作。线程池如ExecutorService简化任务管理,Callable接口允许返回值,Future配合获取异步结果。Java 8引入CompletableFuture支持回调。
|
5天前
|
XML 算法 搜索推荐
Java 中文官方教程 2022 版(四十九)(4)
Java 中文官方教程 2022 版(四十九)
31 0
|
5天前
|
XML 自然语言处理 安全
Java 中文官方教程 2022 版(四十九)(3)
Java 中文官方教程 2022 版(四十九)
22 0