# NDKStudy **Repository Path**: idealcn/NDKStudy ## Basic Information - **Project Name**: NDKStudy - **Description**: ndk开发 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2019-07-13 - **Last Updated**: 2025-02-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 编译so库 Android Studio 编译原生库的默认编译工具是 [CMake](https://cmake.org/)。由于很多现有项目都使用 ndk-build 编译工具包,因此 Android Studio 也支持 [ndk-build](https://developer.android.google.cn/ndk/guides/ndk-build.html?hl=zh_cn)。不过,如果您要创建新的原生库,则应使用 CMake。 ## CMake 使用Android Studio创建一个新的工程,工程名:NDKStudy,包名:com.ideal.ndk.study。选择Phone and Tablet -> Native C++。在NDKStudy/app/src/main目录下自动创建了一个cpp目录。里面包含CMakeLists.txt和一个默认的native-lib.cpp文件。 ### CMakeLists.txt 当运行项目时,在app/build/intermediates/cmake/debug/obj目录下会自动生成不同ABI的so库:libnative-lib.so。 默认`MainActivity`加载了该so库。新建一个`CMakeBuildHelper`类,来加载`libnative-lib.so`库,不在`MainActivity`中加载。 ```java public class CMakeBuildHelper { static { System.loadLibrary("native-lib"); } } ``` 在`CMakeBuildHelper`中定义一个native方法 ```java public native void hello(); ``` 光标置于方法hello之上,通过快捷键alt+enter自动生成该方法的实现。方法实现位于`native-lib.cpp`中。 ```c++ extern "C" JNIEXPORT void JNICALL Java_com_ideal_ndk_study_CMakeBuildHelper_hello(JNIEnv *env, jobject instance) { // TODO } ``` - extern "C" 表明按照c语言规则去链接这个函数。 - JNIEXPORT 将这个函数暴露给外部调用 - JNIENV 指向可用JNI函数表的接口指针。 - jobject 对于静态方法表示的是类本身,非静态方法表示的是当前类的对象。 JNIEnv在c和c++中是有区别的。 > c语言中: > > JNIEnv是指向JNINativeInterface结构的指针,为了访问任何一个JNI函数,该指针需要首先被解引用。因为C代码中的JNI函数不了解当前的JNI环境,JNIEnv实例应该作为第一个参数传递给每一个JNI函数。格式如下: > > ```c > return (*env)->NewStringUTF(env,"hello from JNI"); > ``` > > C++: > > 在C++代码中,JNIEnv实际上是C++类实例,JNI函数以成员函数的形式存在。因为JNI方法已经访问了当前的JNI环境,因此JNI方法调用不要求JNIEnv实例作参数。格式如下: > > ```c++ > return env->NewStringUTF("hello from JNI"); > ``` > > jobject类型参数: > 原生实例方法通过第二个参数获取实例引用,该参数是jobject类型。而静态方法没有与实例绑定,因此通过参数instance获取类引用而不是实例引用,第二个参数是jclass类型。 gradle配置 创建项目时,已经自动在app/build.gradle中配置了CMake。 ```groovy android { externalNativeBuild { cmake { path "src/main/cpp/CMakeLists.txt" version "3.10.2" } } } ``` ## ndk-build 在app/src/main目录下新建文件夹jni。添加文件Android.mk和Application.mk(可选)。 ### 编写Android.mk `Android.mk` 文件位于项目 `jni/` 目录的子目录中,用于向编译系统描述源文件和共享库。它实际上是编译系统解析一次或多次的微小 GNU makefile 片段。`Android.mk` 文件用于定义 [`Application.mk`](https://developer.android.google.cn/ndk/guides/application_mk.html?hl=zh_cn)、编译系统和环境变量所未定义的项目范围设置。它还可替换特定模块的项目范围设置。 `Android.mk` 的语法支持将源文件分组为模块。模块是静态库、共享库或独立的可执行文件。您可在每个 `Android.mk`文件中定义一个或多个模块,也可在多个模块中使用同一个源文件。编译系统只将共享库放入您的应用软件包。此外,静态库可生成共享库。 `Android.mk`文件必须先定义LOCAL_PATH变量,这个变量表示源文件在开发树中的位置。 编译系统提供的宏函数`my-dir`将返回当前目录(Android.mk文件本身所在的目录)的路径。 ``` LOCAL_PATH := $(call my-dir) ```