Article Directory
- I. Android Studio Android.mk used to configure dynamic library summary
- II. Third-party sources of dynamic libraries
- III. Configuring Android.mk build script path
- IV. Precompiled third-party dynamic libraries (Android.mk)
- V. Dynamic link library (DLL set dependent)
- VI. Java native methods and custom code to load dynamic library
- VII. C code to call the DLL function
- VIII. Dynamic libraries to load the version limit (6.0 Android.mk can not be used to configure dynamic libraries)
- IX. Complete code sample
- 1. Build.gradle Configuration Example
- 2. Android.mk Configuration Example
- 3. Java code sample
- 4. C code sample
- 5 operating results. (Android 4.3 phone | 6.0 system can not run)
- X. Blog resources
I. Android Studio Android.mk used to configure dynamic library summary
Android Studio Android.mk used to configure third-party dynamic libraries:
① Android.mk script path settings: Configuration Module Android.mk in build script build.gradle level path to the script;
externalNativeBuild {
ndkBuild{
path "src/main/ndkBuild_Shared/Android.mk"
}
}
② precompiled third-party dynamic libraries: in Android.mk precompiled dynamic library, pay attention to the dynamic library static library to use a different configuration, where a dynamic library, for example:
include $(CLEAR_VARS)
LOCAL_MODULE := add
LOCAL_SRC_FILES := libadd.so
include $(PREBUILT_SHARED_LIBRARY)
③ dynamic link library: pre Android.mk link in static library or dynamic link library, the dynamic attention database static configuration different libraries used, for example where a dynamic library:
LOCAL_SHARED_LIBRARIES := add
④ Java code implementation: declare native methods to load the compiled dynamic library;
⑤ C code implementation: function declaration in the library, call the function in a dynamic library;
II. Third-party sources of dynamic libraries
1. third-party dynamic libraries Source: add.c;
#include <stdio.h>
int add(int a, int b){
return a + b;
}
. 2 Ubuntu cross-compilation process: Reference [Ubuntu] Android NDK development of cross-compiled libraries (Android library dynamic cross compiler | Android static library cross-compiler) , and finally compile libadd.so dynamic libraries, and libadd.a static library;
III. Configuring Android.mk build script path
1 source compiler / packaged configuration principle: [development] Android NDK Android Studio NDK's configuration (source compiler configuration | build script configuration | packaged configuration | CMake configuration | ndkBuild configuration):. I compiler source code configuration
2. build script path configuration principle: [development] Android NDK Android Studio NDK's configuration (source compiler configuration | build script configuration | packaged configuration | CMake configuration | ndkBuild configuration): II build script configuration.
3. Here Android.mk disposed directly build script path: to omit nothing configuration, leaving only the configuration NDK;
apply plugin: 'com.android.application'
android {
...
defaultConfig {
...
// 配置 AS 工程中的 C/C++ 源文件的编译
// defaultConfig 内部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件编译参数
// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 构建脚本的路径
externalNativeBuild {
/*cmake {
cppFlags ""
//配置编译 C/C++ 源文件为哪几个 CPU 指令集的函数库 (arm , x86 等)
abiFilters "armeabi-v7a"
}*/
ndkBuild{
abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/
}
}
//配置 APK 打包 哪些动态库
// 示例 : 如在工程中集成了第三方库 , 其提供了 arm, x86, mips 等指令集的动态库
// 那么为了控制打包后的应用大小, 可以选择性打包一些库 , 此处就是进行该配置
ndk{
// 打包生成的 APK 文件指挥包含 ARM 指令集的动态库
abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/
}
}
// 配置 NDK 的编译脚本路径
// 编译脚本有两种 ① CMakeList.txt ② Android1.mk
// defaultConfig 内部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件编译参数
// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 构建脚本的路径
externalNativeBuild {
// 配置 CMake 构建脚本 CMakeLists.txt 脚本路径
/*cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}*/
// 配置 Android1.mk 构建脚本路径
ndkBuild{
path "src/main/ndkBuild_Shared/Android.mk"
//path "src/main/ndkBuild_Static/Android.mk"
}
}
...
}
...
IV. Precompiled third-party dynamic libraries (Android.mk)
1. Clear variables: (begin the Add module configuration)
① Role: To configure the new module must first remove LOCAL_XXX variable;
② exceptions: One exception is not cleared LOCAL_PATH variable;
③ identification module begins: the include $ (CLEAR_VARS), mark the beginning of a general configuration of a module;
④ end module: the include $ (XXX_STATIC_LIBRARY) / the include $ (XXX_SHARED_LIBRARY) typically arranged as a module end flag;
include $(CLEAR_VARS)
2. Configure dynamic library name:
① automatic generation of library Name premise: no LOCAL_MODULE_FILENAME configuration, it will automatically generate a library name;
② dynamic library naming conventions: the LOCAL_MODULE name basis, add lib prefix (if no longer add the prefix in front of lib) and .so suffix;
③ a dynamic library name: libadd.so;
LOCAL_MODULE := add
3 Set precompiled dynamic library path:
LOCAL_SRC_FILES := libadd.so
4. Set precompiled dynamic library: (end of the Add Module Configuration)
include $(PREBUILT_SHARED_LIBRARY)
5. Complete third-party dynamic libraries pre-compiled module configuration:
# II . 预编译第三方动态库
# 1 . 清除变量 ( add 模块配置开始 )
# ① 作用 : 配置新的模块之前都要先清除 LOCAL_XXX 变量
# ② 例外情况 : 但是不会清除 LOCAL_PATH 变量
# ③ 模块开始 : include $(CLEAR_VARS)
# ④ 模块结尾 : include $(XXX_STATIC_LIBRARY) / include $(XXX_SHARED_LIBRARY)
include $(CLEAR_VARS)
# 2 . 配置动态库名称
# ① 自动生成函数库名称前提 : 没有 LOCAL_MODULE_FILENAME 配置 , 就会自动生成函数库名称
# ② 动态库命名规则 : 在 LOCAL_MODULE 名称基础上 , 添加 lib 前缀和 .so 后缀
# ③ 生成动态库名称 : libadd.so
LOCAL_MODULE := add
# 3 . 预编译的动态库路径
LOCAL_SRC_FILES := libadd.so
# 4 . 设置预编译动态库 ( add 模块配置结束 )
include $(PREBUILT_SHARED_LIBRARY)
V. Dynamic link library (DLL set dependent)
Set the dynamic library dependencies:
① dependencies: compile native-lib module, you need to add a static link library;
② add dynamic libraries: the Add module is a pre-compiled libraries, precompiled third-party content is dynamic libraries introduced;
LOCAL_SHARED_LIBRARIES := add
# IV . 配置动态库模块
# 1 . 清除变量 ( native-lib 模块配置开始 )
# ① 作用 : 配置新的模块之前都要先清除 LOCAL_XXX 变量
# ② 例外情况 : 但是不会清除 LOCAL_PATH 变量
include $(CLEAR_VARS)
# 2 . 配置动态库名称
# ① 自动生成函数库名称前提 : 没有 LOCAL_MODULE_FILENAME 配置 , 就会自动生成函数库名称
# ② 动态库命名规则 : 在 LOCAL_MODULE 名称基础上 , 添加 lib 前缀 和 .so 后缀
# ③ 生成动态库名称 : libnative-lib.so
LOCAL_MODULE := native-lib
# 3 . 编译的源文件
LOCAL_SRC_FILES := native-lib.c
# 4 . 设置动态依赖库
# ① 依赖库 : 编译 native-lib 模块 , 需要链接 add 动态库
# ② add 动态库 : add 模块是一个预编译库 , 预编译内容是引入的第三方动态库
LOCAL_SHARED_LIBRARIES := add
# 5 . 链接日志库
LOCAL_LDLIBS := -llog
# 6 . 设置预编译动态库 ( native-lib 模块配置结束 )
include $(BUILD_SHARED_LIBRARY)
VI. Java native methods and custom code to load dynamic library
1. Declare and use native methods:
public native String stringFromJNI();
2. load dynamic library: here the two dynamic libraries to load;
System.loadLibrary("add");
System.loadLibrary("native-lib");
VII. C code to call the DLL function
1. Declare external dynamic library method:
//声明 libadd.so 动态库中的方法
extern int add(int a, int b);
2 DLL calls the method:
//调用动态库中的函数
int sum = add(1, 2);
VIII. Dynamic libraries to load the version limit (6.0 Android.mk can not be used to configure dynamic libraries)
1. Problem Description:
6.0 release, the use of third-party Android.mk build scripts precompiled dynamic library, if used in the program System.loadLibrary load the precompiled libraries will error;
6.0 The following mobile versions (not including the 6.0 version), System.loadLibrary need to manually load-dependent dynamic library;
Mobile version 6.0 and above, System.loadLibrary will automatically load-dependent dynamic libraries can not be loaded at this time dependent dynamic library, or an error occurs;
2 error message: more than 6.0 Android.mk phone can not be used to load dynamic library, once loaded collapse; reported the following error;
2020-02-13 01:58:38.044 23033-23033/kim.hsl.mk E/AndroidRuntime: FATAL EXCEPTION: main
Process: kim.hsl.mk, PID: 23033
java.lang.UnsatisfiedLinkError: dlopen failed: library "Y:/002_WorkSpace/001_AS/006_NDK_Android_mk/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libadd.so" not found
at java.lang.Runtime.loadLibrary0(Runtime.java:977)
at java.lang.System.loadLibrary(System.java:1567)
at kim.hsl.mk.MainActivity.<clinit>(MainActivity.java:19)
...
3 solutions: more than 6.0 phones can only be used to configure CMake dynamic library, or use the static library;
. 4 CMake configuration scheme reference: [] NDK Android NDK development of cross-compiler (Ubuntu in cross-compile a dynamic library | Android Studio is configured to use a third-party DLL)
IX. Complete code sample
1. Build.gradle Configuration Example
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.0"
defaultConfig {
applicationId "kim.hsl.mk"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// 配置 AS 工程中的 C/C++ 源文件的编译
// defaultConfig 内部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件编译参数
// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 构建脚本的路径
externalNativeBuild {
/*cmake {
cppFlags ""
//配置编译 C/C++ 源文件为哪几个 CPU 指令集的函数库 (arm , x86 等)
abiFilters "armeabi-v7a"
}*/
ndkBuild{
abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/
}
}
//配置 APK 打包 哪些动态库
// 示例 : 如在工程中集成了第三方库 , 其提供了 arm, x86, mips 等指令集的动态库
// 那么为了控制打包后的应用大小, 可以选择性打包一些库 , 此处就是进行该配置
ndk{
// 打包生成的 APK 文件指挥包含 ARM 指令集的动态库
abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/
}
}
// 配置 NDK 的编译脚本路径
// 编译脚本有两种 ① CMakeList.txt ② Android1.mk
// defaultConfig 内部的 externalNativeBuild 配置的是配置 AS 工程的 C/C++ 源文件编译参数
// defaultConfig 外部的 externalNativeBuild 配置的是 CMakeList.txt 或 Android1.mk 构建脚本的路径
externalNativeBuild {
// 配置 CMake 构建脚本 CMakeLists.txt 脚本路径
/*cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}*/
// 配置 Android1.mk 构建脚本路径
ndkBuild{
path "src/main/ndkBuild_Shared/Android.mk"
//path "src/main/ndkBuild_Static/Android.mk"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
2. Android.mk Configuration Example
# I . 保存构建脚本的路径 , 并赋值给 LOCAL_PATH 变量
# ① 内置函数 : my-dir 是 NDK 内置的函数 , 获取当前的目录路径
# 在该案例中就是 Android.mk 文件所在的目录的绝对路径 , 工程根目录/app/src/main/cpp
# 将该目录赋值给 LOCAL_PATH 变量
# 所有的 Android1.mk 的第一行配置都是该语句
LOCAL_PATH := $(call my-dir)
# II . 预编译第三方动态库
# 1 . 清除变量 ( add 模块配置开始 )
# ① 作用 : 配置新的模块之前都要先清除 LOCAL_XXX 变量
# ② 例外情况 : 但是不会清除 LOCAL_PATH 变量
# ③ 模块开始 : include $(CLEAR_VARS)
# ④ 模块结尾 : include $(XXX_STATIC_LIBRARY) / include $(XXX_SHARED_LIBRARY)
include $(CLEAR_VARS)
# 2 . 配置动态库名称
# ① 自动生成函数库名称前提 : 没有 LOCAL_MODULE_FILENAME 配置 , 就会自动生成函数库名称
# ② 动态库命名规则 : 在 LOCAL_MODULE 名称基础上 , 添加 lib 前缀 和 .so 后缀
# ③ 生成动态库名称 : libadd.so
LOCAL_MODULE := add
# 3 . 预编译的动态库路径
LOCAL_SRC_FILES := libadd.so
# 4 . 设置预编译动态库 ( add 模块配置结束 )
include $(PREBUILT_SHARED_LIBRARY)
# III . 打印变量值
# 打印 LOCAL_PATH 值
# Build 打印内容 : LOCAL_PATH : Y:/002_WorkSpace/001_AS/005_NDK_Compile/app/src/main/cpp
# 编译 APK 时会在 Build 中打印
$(info LOCAL_PATH : $(LOCAL_PATH))
# IV . 配置动态库模块
# 1 . 清除变量 ( native-lib 模块配置开始 )
# ① 作用 : 配置新的模块之前都要先清除 LOCAL_XXX 变量
# ② 例外情况 : 但是不会清除 LOCAL_PATH 变量
include $(CLEAR_VARS)
# 2 . 配置动态库名称
# ① 自动生成函数库名称前提 : 没有 LOCAL_MODULE_FILENAME 配置 , 就会自动生成函数库名称
# ② 动态库命名规则 : 在 LOCAL_MODULE 名称基础上 , 添加 lib 前缀 和 .so 后缀
# ③ 生成动态库名称 : libnative-lib.so
LOCAL_MODULE := native-lib
# 3 . 编译的源文件
LOCAL_SRC_FILES := native-lib.c
# 4 . 设置静态依赖库
# ① 依赖库 : 编译 native-lib 模块 , 需要链接 add 静态库
# ② add 静态库 : add 模块是一个预编译库 , 预编译内容是引入的第三方动态库
LOCAL_SHARED_LIBRARIES := add
# 5 . 链接日志库
LOCAL_LDLIBS := -llog
# 6 . 设置预编译动态库 ( native-lib 模块配置结束 )
include $(BUILD_SHARED_LIBRARY)
# V . 配置动态库与静态库区别
# 1 . 预编译时的路径不一致 :
# ① 动态库路径 : libadd.so
# ② 静态库路径 : libadd.a
# 2 . 预编译时结束配置不一致 :
# ① 动态库配置 : include $(PREBUILT_SHARED_LIBRARY)
# ② 静态库配置 : include $(PREBUILT_STATIC_LIBRARY)
# 3 . 链接依赖库时配置不一致 :
# ① 动态库依赖配置 : LOCAL_SHARED_LIBRARIES
# ② 静态库依赖配置 : LOCAL_STATIC_LIBRARIES
3. Java code sample
package kim.hsl.mk;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static {
// 1 . 加载动态库的情况 : ( 必须不能注释下面的代码 )
// ① 6.0 以下的版本 : 需要手动加载依赖库 libadd.so
// ② 6.0 以上的版本 : 无法使用 Android.mk 构建脚本加载第三方动态库
// 此情况下, 无论是否手动加载 libadd.so 都会报错
//
// 2 . 加载静态库的情况 : ( 必须注释掉下面的这行代码 )
System.loadLibrary("add");
System.loadLibrary("native-lib");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
public native String stringFromJNI();
}
4. C code sample
#include <jni.h>
#include <stdio.h>
#include <stdlib.h>
#include <android/log.h>
//调用 libadd.so 动态库中的方法
extern int add(int a, int b);
JNIEXPORT jstring JNICALL
Java_kim_hsl_mk_MainActivity_stringFromJNI(
JNIEnv *env,
jobject obj) {
//调用动态库中的函数
int sum = add(1, 2);
// Logcat 打印计算结果
__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "Native Caculate : %d + %d = %d", 1, 2, sum);
//将加法运算转为字符串
char str[50] = "0";
//字符串格式化
sprintf(str, "Native Caculate : Static Library : %d + %d = %d", 1, 2, sum);
return (*env)->NewStringUTF(env, str);
}
5 operating results. (Android 4.3 phone | 6.0 system can not run)
X. Blog resources
Blog Resources Download: https://download.csdn.net/download/han1202012/12157107
Sample Code GitHub Address: https://github.com/han1202012/006_NDK_Android_mk