JNI开发

文章讲述了在AndroidNative开发中,使用CMake和Android.mk两种不同的构建系统来生成SO库文件的过程。CMake无需额外命令,自动生成SO并打包,而Android.mk则需要执行命令手动构建。文中还涉及到JNI的动态注册和静态注册方法,并给出了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

文件结构(选中的为生成的)
在这里插入图片描述
CMake构建不需要执行命令,会自动生成so文件打包进apk
Android mk构建需要执行命令生成so文件,再打包进apk。命令如下。


# 在jni目录下执行
# 生成com_demo_cppproject_OtherNdkTest.h头文件
javac -h ./ ../java/com/demo/cppproject/OtherNdkTest.java
# 构建出so文件
ndk-build NDK_PROJECTPATH=. NDK_APPLICATION_MK=Application.mk NDK_BUILD_SCRIPT=Android.mk NDK_LIBS_OUT=..\jniLibs\

MainActivity.java

package com.demo.cppproject;
public class MainActivity extends AppCompatActivity {

    // Used to load the 'cppproject' library on application startup.
    static {
        System.loadLibrary("cppproject");
        System.loadLibrary("DEMO");
    }

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        TextView tv = binding.sampleText;

        String temp = stringFromJNI() + "\n" + new MyNdkTest().getData() + "\n"
                + new OtherNdkTest().getName() + "==" + new OtherNdkTest().count();

        tv.setText(temp);
    }

    public native String stringFromJNI();
}

MyNdkTest.java

package com.demo.cppproject;

public class MyNdkTest {
    public native String getData();
}

OtherNdkTest.java

package com.demo.cppproject;

public class OtherNdkTest {
    public native String getName();

    public native  int  count();
}

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.

cmake_minimum_required(VERSION 3.22.1)

# Declares and names the project.

project("cppproject")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
        cppproject

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

# 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.
        cppproject

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

native-lib.cpp

#include <assert.h>
#include <jni.h>
#include <string>

#ifndef _Included_com_demo_cppproject_MyNdkTest
#define _Included_com_demo_cppproject_MyNdkTest
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL Java_com_demo_cppproject_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "静态注册!cmake构建!Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

// 定义的对应java中的定义native方法, 采用动态注册
//extern "C" JNIEXPORT jstring JNICALL Java_com_demo_cppproject_MyNdkTest_getData(
JNIEXPORT jstring JNICALL getData(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "动态注册!cmake构建!get data";
    return env->NewStringUTF(hello.c_str());
}

//需要动态注册的native方法所在的类
#define JNIREG_CLAS_MAIN "com/demo/cppproject/MyNdkTest"

//创建JNINativeMethod的数组,用来存放,JNINativeMethod结构变量,
// JNINativeMethod结构存放:注册的native方法,对应的签名,C++/C的对应的JNI方法
static JNINativeMethod gMethods[] = {
        {"getData", "()Ljava/lang/String;", (void*)getData}
};

static int registerNativeMethods(JNIEnv *env, const char *className,
                                 JNINativeMethod *gMethods, int numMethods) {
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/***
* 注册native方法
*/
static int registerNatives(JNIEnv *env) {
    if (!registerNativeMethods(env, JNIREG_CLAS_MAIN, gMethods,
                               sizeof(gMethods) / sizeof(gMethods[0]))
            ) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

/**
* 如果要实现动态注册,这个方法一定要实现
* 动态注册工作在这里进行
*/
JNIEXPORT jint

JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;

    if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);

    if (!registerNatives(env)) { //注册
        return -1;
    }
    result = JNI_VERSION_1_4;
    return result;
}

#ifdef __cplusplus
}
#endif
#endif

demo.cpp

//
// Created by Admins on 2023/5/6.
//
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#include <string>
/* Header for class com_demo_cppproject_MyNdkTest */

#ifndef _Included_com_demo_cppproject_OtherNdkTest
#define _Included_com_demo_cppproject_OtherNdkTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_demo_cppproject_MyNdkTest
 * Method:    getData
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_demo_cppproject_OtherNdkTest_getName
        (JNIEnv *env, jobject){
    std::string hello = "静态注册!Android mk构建!Hi from C++";
    return env->NewStringUTF(hello.c_str());
}

/*
 * Class:     com_demo_cppproject_MyNdkTest
 * Method:    count
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_demo_cppproject_OtherNdkTest_count
        (JNIEnv *env, jobject){
    return 11;
}

#ifdef __cplusplus
}
#endif
#endif

Android.mk

# Android.mk 参数

# 设置工作目录,它用于在开发tree中查找源文件。宏my-dir由Build System提供,会返回Android.mk文件所在的目录
LOCAL_PATH := $(call my-dir)

# CLEAR_VARS变量由Build System提供。指向一个指定的GNU Makefile,由它负责清理LOCAL_xxx类型文件,
# 但不是清理LOCAL_PATH
# 所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能便面相互影响,这一操作必须有
include $(CLEAR_VARS)

# LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块,名字必须唯一且不包含空格
# Build System 会自动添加适当的前缀和后缀。例如,demo,要生成动态库,则生成libdemo.so。
# 但请注意:如果模块名字被定义为libabd,则生成libabc.so。不再添加前缀。
LOCAL_MODULE := DEMO


# 指定参与模块编译的C/C++源文件名。不必列出头文件,build System 会自动帮我们找出依赖文件。
# 缺省的C++ 源码的扩展名为.cpp。
LOCAL_SRC_FILES := demo.cpp


# BUILD_SHARED_LIBRARY是Build System提供的一个变量,指向一个GUN Makefile Script。它负责收集自从上次调用include $(CLEAR_VARS)后的所有LOCAL_xxxxinx。并决定编译什么类型
# 1. BUILD_STATIC_LIBRARY:编译为静态库
# 2. BUILD_SHARED_LIBRARY:编译为动态库
# 3. BUILD_EXECUTABLE:编译为Native C 可执行程序
# 4. BUILD_PREBUILT:该模块已经预先编译
include $(BUILD_SHARED_LIBRARY)

# 命令:
# ndk-build NDK_PROJECTPATH=. NDK_APPLICATION_MK=Application.mk NDK_BUILD_SCRIPT=Android.mk NDK_LIBS_OUT=..\jniLibs\

Application.mk

#Application.mk 参数
APP_MODULES := DEMO
# 默认生成支持的多种类型.so
APP_ABI := all

APP_STL := c++_shared
#APP_PLATFORM := android-16不配置,打包.so会出错
APP_PLATFORM := android-19

参考:
JNI中native方法的几种注册方式
[NDK]-搭建ndk-build环境
Android Studio 4.0.+NDK .so库生成打包

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值