注:默认你以及安装好NDK开发的所有环境,包括cmake插件,没配置的点这。
一.Android studio自动创建demo分析
1.新建一个Project,选择Native C++项目类创建;
2.项目会默认创建出3个关键文件,cpp目录的native-lib.cpp、CMakeList.txt、以及入口类 MainActivity.
3.分析一下这三个类的关键代码,理解了再进行模仿
1.MainActivity.java入口类:
package com.android.projectforblog;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.android.projectforblog.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// Used to load the 'projectforblog' library on application startup.
static {
System.loadLibrary("projectforblog");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
}
/**
* A native method that is implemented by the 'projectforblog' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}
System.loadLibrary("projectforblog")加载so库,这个库是项目build或者Run起来后在 “app -> build -> intermediates -> cmake -> debug/release -> obj -> (具体架构)arm64-v8a/armeabi-v7a/x86/x86_64” 目录下生成的, 名字是在开发者自己在CMakeLists.txt中定义,如果在当前项目直接引用则是定义的名字,但是系统在build生成的so文件时会给你文件名字默认加上前缀lib。
stringFromJNI是本地方法的声明,java代码可以直接调用它,它调用projectforblog库中对应的头文件声明,头文件声明方法再调用对应的c文件;如果c库中没有对应的.h头文件,则会直接调用对应的.c/.cpp文件
2.native-lib.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_android_projectforblog_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
-
extern "C" : 一方面
extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或其它模块中使用;另一方面,这是一个C++语言的特殊语法,用于指定函数的名称不需要进行C++的命名规则修饰。被extern "C"修饰的变量和函数是按照C语言方式编译和连接的 -
JNIEXPORT 是jni.h中的一个被定义的宏,用于
指定函数的可见性和调用约定。 它会根据操作系统的不同,展开为不同的关键字。例如,在 Windows 平台上为__declspec(dllexport)关键字,用于指定函数的导出;在 Linux 平台上,它会展开为attribute((visibility("default")))关键字,用于指定函数的可见性; -
jstring 是jni.h中对应JAVA语言
String基本结构的类型,更多内容看文章三。 -
JNICALL 是jni.h中的一个被定义的宏。意思为
JNI调用某方法。 -
Java_com_android_projectforblog_MainActivity_stringFromJNI指JNICALL调用的方法名,也是java中的native方法名。命名规则:
"Java"+包名+类名+方法名。
3.CMakeLists.txt
# 设置cmake版本,得看anroid studio配置cmake创建是什么版本,创建会自动添加。
cmake_minimum_required(VERSION 3.22.1)
# 项目名字,注意,不是so库名字,NDK这个项目有时候会独立一个module,可以配置版本。
project(projectforblog VERSION 1.0)
add_library( # Sets the name of the library.
# 设置库名字
projectforblog
# 设置为共享库
SHARED
# 填写的是库源代码的路径
native-lib.cpp)
find_library(
#如果要打印log,这段代码照搬就好了,这个lib库是ndk默认提供的。
# 要寻找的库,路径存储位置
log-lib
# 要寻找的库的名字
log)
target_link_libraries(
# 定义最终要链接的库名字
projectforblog
# log-lib不是库的名字,而是log库的位置路径。
${log-lib})
CMakeLists.txt 是一个 CMake 工程的配置文件,用于描述项目的编译、链接和安装等相关信息。以下是一些常用的参数说明:
-
CMAKE_MINIMUM_REQUIRED:指定 CMake 的最低版本要求,填你配置的cmake的版本号 -
PROJECT:指定项目的名称,还可以设置项目的版本号、类型(例如可执行文件、静态库或动态库)、使用的语言(例如C或C++)。 -
INCLUDE_DIRECTORIES:指定头文件搜索路径,例如: include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") -
ADD_LIBRARY:可以用来添加normal library和imported library两种类型的库文件。 Normal library 是指在当前 CMake 项目中编译生成的库文件,可以使用 add_library 命令来创建。它可以是静态库(.a 或 .lib)或动态库(.so 或 .dll)。静态库是在编译时链接到目标程序中的库,而动态库是在运行时动态加载的库。假设我们有一个名为 mylib 的 C++ 库,包含两个源文件 foo.cpp 和 bar.cpp,需要编译成静态库和动态库。可以使用以下 CMakeLists.txt 文件来创建 normal library,CMakeLists.txt
# SHARED意味着编译生成静态库 add_library(mylib_static STATIC foo.cpp bar.cpp) # SHARED意味着编译生成动态库 add_library(mylib_shared SHARED foo.cpp bar.cpp) -
Imported library是指在当前 CMake 项目外部编译生成的库文件,可以使用 add_library 命令的 IMPORTED 参数来创建。在使用时,需要通过 target_link_libraries 命令将其链接到目标文件中。假设我们有一个名为 libcurl 的第三方库,已经编译成了 libcurl.a 静态库和 libcurl.so 动态库。现在我们需要在我们的 C++ 项目中链接这个库。可以使用以下 CMakeLists.txt 文件来创建 imported library:
# CMakeLists.txt add_library(curl STATIC IMPORTED) set_target_properties( curl PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libcurl.a ) target_link_libraries(curl)
注:上面只写了静态库.a的规则写法,动态库的直接改成.so就好了,使用 set_target_properties 命令设置其 IMPORTED_LOCATION 属性为 libcurl.a 的路径
-
TARGET_LINK_LIBRARIES:指定链接的库文件。 -
SET:设置变量的值,例如#set(CMAKE_ANDROID_ARCH_ABI arm64-v8a) -
LINK_DIRECTORIES:指定库文件搜索路径。
-
INSTALL:指定安装规则。
-
FIND_PACKAGE:查找外部库文件的位置和版本信息。
-
MESSAGE:输出消息。
-
ADD_EXECUTABLE:添加可执行文件。 以上是一些常用的 CMakeLists.txt 参数说明。在实际使用中,可以根据项目的需要来选择和配置相应的参数。
4.build之后生成的so库,理论上这个so库也是可以提供给其他应用进行调用
二.总结
1.展示了如何通过Android Studio自己创建一个so库,官方默认demo在build之后就可以看到有so库
2.解释了核心的3个文件的作用和So库的引用方式。
3.展示了自定义so库引入第三方so库的方法。
4.由于篇幅问题,上面只讲述了基础的demo,实际开发中不可能只是获取一个String就可以了的,可能要获取int,float,array等数据类型,此外,也有可能要提供对象等数据才能返回结果等,因此下章节讲述完整版的实际开发,比如作为提供So库的项目,应该是app_moudule + so_library_moudule的形似,在so_library_module中提供so库和java调用管理类,在app_module中进行自我测试,测试通过再打包为aar提供给第三方开发者