欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 家装 > Android NDK 集成调用第三方SO库(标准SO库和非标准SO库)

Android NDK 集成调用第三方SO库(标准SO库和非标准SO库)

2025/2/26 1:31:55 来源:https://blog.csdn.net/sziitjin/article/details/145823917  浏览:    关键词:Android NDK 集成调用第三方SO库(标准SO库和非标准SO库)

想要进阶高级Android开发工程师,NDK开发是必须要掌握的技能之一,本文以集成第三方SO库为开端,带大家入门NDK开发,主要包含一下内容:

1、NDK工程创建;

2、编写和打包Android标准SO库;

3、集成Android标准SO库;

4、集成Android非标准SO库。

一、NDK工程创建

1、什么是NDK(Native Development Kit)?

NDK是一个为Android平台提供支持的开发工具包,专门用于开发本地(Native)代码的。通过NDK,开发者可以编写C、C++代码并将其编译成可以在Android设备上运行的共享库(.so文件)。简单来说,就是通过NDK让Android可以使用C、C++代码。

2、什么是JNI(Java Native Interface)?

JNI是Java与其他编程语言(如C、C++)之间的接口。它允许Java代码和本地代码(通常是C或C++)进行互相调用。
当开发者需要在Android应用中使用Java与C/C++代码交互时,就需要使用JNI。它提供了一种桥接机制,让Java代码能够调用C/C++库。

3、什么是CMake?

CMake 是一个跨平台的自动化构建系统工具,它主要用于管理和生成项目的构建过程。可以简单的把CMake理解Android工程的Gradle。

4、创建Native Android工程

 Native工程创建好后,会自动生成CMake文件,在cpp文件目录想编写C/C++代码;在java目录下编写java代码。

1)简单的了解一下CMake文件代码:

# 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)#当前工程名
project("nativet0224")#添加一个库(动态库SHARED,静态库STATIC)
add_library(nativet0224 #库的名字--->nativet0224.soSHARED #动态库#cpp的源文件:把cpp源文件编译成libnative-lib.so库native-lib.cpp)#查找一个NDK工具中的动态库(liblog.so)
find_library(log-liblog)#nativet0224是我们的总库,也就是我们在apk/lib/nativet0224.so
#然后把log库链接到总库中去,总库的cpp代码就可以使用android/log.h的库实现代码了
target_link_libraries(nativet0224 #被链接的总库#链接的具体库${log-lib})

2)native-lib代码

native-lib.cpp主要是起到一个桥接作用,使用Java层连接native调用C++层。当我们需要让java层调用C++代码时,需要先在java层定义一个Native方法,让后通过快捷键,在native-lib.cpp自动生成桥接方法。

#include <jni.h>
#include <string>//Java层连接native调用C++层
extern "C" JNIEXPORT jstring JNICALL
Java_com_nativet0224_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

3)Java层定义Native方法

Native工程创建好后,会在MainActivity里面自动生成调用Native层的默然方法。

public class MainActivity extends AppCompatActivity {// 加载nativet0224.so库,项目编译后apk的lib目录下会自动打包生成nativet0224.so库static {System.loadLibrary("nativet0224");}private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());TextView tv = binding.sampleText;// 调用native方法tv.setText(stringFromJNI());}//声明一个native方法,供Java层调用public native String stringFromJNI();
}

4)自定义NativeLib.java

真实的项目开发中,我们会将Java层调用Native方法的定义统一封装到NativeLib.java

public class NativeLib {// 加载nativet0224.so库,项目编译后apk的lib目录下会自动打包生成nativet0224.so库static {System.loadLibrary("nativet0224");}//声明一个native方法,供Java层调用public native String testCallNative();
}

定义testCallNative()方法后,将鼠标放到 testCallNative()方法上,Androidstudio自动提示在native层native-lib.cpp创建桥接方法。

简单的写个字符串“Hello my Native.”返回,这里用的是C++ 语言开发,需要花点时间入门一下,或者直接AI帮写。

#include <jni.h>
#include <string>//Java层连接native调用C++层
extern "C" JNIEXPORT jstring JNICALL
Java_com_nativet0224_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_nativet0224_NativeLib_testCallNative(JNIEnv *env, jobject thiz) {// TODO: implement testCallNative()std::string hello = "Hello my Native.";return env->NewStringUTF(hello.c_str());
}

5)Java层调用

public class MainActivity extends AppCompatActivity {private ActivityMainBinding binding;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityMainBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());NativeLib nativeLib = new NativeLib();String result = nativeLib.testCallNative();Log.d("NDK", result);}
}

至此,一个简单的Android NDK工程已完成,实现了Java层与C++层的交互。

二、编写和打包Android标准SO库

将上面的Android NDK工程打包,即可生成Android标准SO库。

编译出的 .so 文件位于以下目录: 

app/build/intermediates/cmake/debug/obj/<architecture>/lib<library_name>.so

三、集成Android标准SO库

重新创建一个Android工程,集成我们打包出来的libnativet0224.so;

1)在新的Android工程的main目录下,创建jniLibs文件夹,将打包好的libnativet0224.so复制到该目录下。

2)在build.gradle文件中声明使用

android {//.....sourceSets {main {// 指定 jniLibs 路径jniLibs.srcDirs = ['src/main/jniLibs']}}
}

3) 自定义NativeLib.java

将Java层调用Native方法的定义统一封装到NativeLib.java,同Native工程的NativeLib.java。

但是这里要注意了,NativeLib.java的包名要同打包SO库的NativeLib.java的包名一致,不让对应的Native方法会对应不上。

我们先做一个错误的示范:

将NativeLib.java放在新项目的包名下,

在MainActivity调用:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);NativeLib nativeLib = new NativeLib();String result = nativeLib.testCallNative();Log.d("NDK", result);}
}

 执行报错:

java.lang.UnsatisfiedLinkError: No implementation found for java.lang.String com.nativet0224java.NativeLib.testCallNative() (tried Java_com_nativet0224java_NativeLib_testCallNative and Java_com_nativet0224java_NativeLib_testCallNative__)

 原因是,新项目的NativeLib.java是在com.nativet0224java包名下的,故新项目的NativeLib.java的public native String testCallNative()方法生成的Native方法命名是:Java_com_nativet0224java_NativeLib_testCallNative

而SO库中testCallNative()方法生成的Native方法命名是:

Java_com_nativet0224_NativeLib_testCallNative,

导致新项目无法在SO库中找到对应的Native方法,需要将新项目的NativeLib.java写在com.nativet0224包名下。

修改后重新编译,项目正常运行:

四、集成Android非标准SO库

然而在真实项目开发过程中,我们集成的SO库往往是第三方开发的,很大可能并不是标准的SO库(即不存在native-lib.cpp桥接文件,或native-lib.cpp桥接文件里面没有定义符合Android命名(Java_com_nativet0224_NativeLib_testCallNative)的桥接方法)。

这时候我们就需要自己创建一个Native Module去集成非标准的SO库。

1)在上面NDK工程增加test C++文件,并创建一个test方法

test.cpp

//
// Created by mypc on 2025-02-24.
//#include "test.h"Text0224::Text0224() {}Text0224::~Text0224() {}int Text0224::test(int a, int b) {return a + b;
}

test.h

//
// Created by mypc on 2025-02-24.
//#ifndef NATIVET0224_TEST_H
#define NATIVET0224_TEST_Hclass Text0224 {
public:Text0224();~Text0224();int test(int a, int b);};#endif //NATIVET0224_TEST_H

2)在上面NDK工程的CMake中配置将text.cpp打包到SO库中

重新打包,生成的新的libnativet0224.so就会包含test.cpp在里面

3)在Java工程创建一个Native Module

4)在nativelib模块的main目录下,创建jniLibs文件夹,将新打包好的包含text.cpp文件的libnativet0224.so复制到该目录下。

5)在 nativelib模块的CMake文件中添加nativet0224动态库

# 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)project("nativelib")#添加nativet0224动态库
add_library(nativet0224 SHARED IMPORTED)
#指定nativet0224动态库的路径
set_target_properties(nativet0224  PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libnativet0224.so)add_library(nativelibSHAREDnativelib.cpp)find_library(log-liblog)target_link_libraries(nativelib#链接的nativet0224库到打包出来的新的nativelib.so库里面nativet0224${log-lib})

6)在nativelib模块自动生成的NativeLib.java文件中增加调用Native层text.cpp类的test方法的桥接方法。参数和返回值要对应上text.cpp类的test方法。

public native int callTest(int a, int b);

package com.nativelib;public class NativeLib {// Used to load the 'nativelib' library on application startup.static {System.loadLibrary("nativelib");}/*** A native method that is implemented by the 'nativelib' native library,* which is packaged with this application.*/public native String stringFromJNI();public native int callTest(int a, int b);
}

 快捷键自动创建Native层的桥接方法

#include <jni.h>
#include <string>extern "C" JNIEXPORT jstring JNICALL
Java_com_nativelib_NativeLib_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jint JNICALL
Java_com_nativelib_NativeLib_callTest(JNIEnv *env, jobject thiz, jint a, jint b) {// TODO: implement callTest()
}

7)在nativelib模块中cpp目录下创建text.h文件,可以直接复制上面的text.h文件

text.h

//
// Created by mypc on 2025-02-24.
//#ifndef NATIVET0224_TEST_H
#define NATIVET0224_TEST_Hclass Text0224 {
public:Text0224();~Text0224();int test(int a, int b);};#endif //NATIVET0224_TEST_H

8)在 nativelib模块的Native层的桥接方法中谁用text.cpp的test方法

#include <jni.h>
#include <string>//Java层连接native调用C++层
extern "C" JNIEXPORT jstring JNICALL
Java_com_nativet0224_MainActivity_stringFromJNI(JNIEnv* env,jobject /* this */) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_nativet0224_NativeLib_testCallNative(JNIEnv *env, jobject thiz) {// TODO: implement testCallNative()std::string hello = "Hello my Native.";return env->NewStringUTF(hello.c_str());
}

9)Java层调用NativeLib.java的callTest桥接方法

在app模块的build.gradle中增加对native模块的依赖

implementation project(path: ':nativelib')

MainActivity中调用

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);com.nativelib.NativeLib nativeLib = new com.nativelib.NativeLib();int result = nativeLib.callTest(2, 3);Log.d("NDK", "result = " + result);}
}

至此,实现了Android集成非标准的SO库。 

注意:我这里演示的集成Android标准SO库和集成Android非标准SO库,用的是同一个工程,所以在集成Android非标准SO库时,需要将集成Android标准SO库时在app模块main目录下,创建jniLibs文件夹和libnativet0224.so删除,避免存在来个同名libnativet0224.so。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词