在Android studio2.2以及以上,构建原生库的默认工具是cmake。
CMake是一个跨平台的构建工具,可以使用简单的语句来描述所有平台的安装(编译过程)。
能够输出各种各样的makefile或者project文件。cmake并不直接构建出最终的软件,而是产生其他工具的脚本(如makefile)。然后再根据这个工具的构建方式使用。
cmake是一个比make更高级的编译配置工具,可以根据不同的平台、不同的编译器生成相对应的makefile或vcproject项目,从而达到跨平台的目的。
c/c++ 编译成动态库和静态库的三种方式
- 使用Linux gcc编译 实现ndk交叉编译
- Android studio下使用mk实现ndk交叉编译(这种方式比较老旧,现在已被Cmake取代)
- Android studio下使用Cmake实现ndk交叉编译
linux实现交叉编译
在linux环境中使用wget命令进行下载:
wget https://dl.google.com/android/repository/android-ndk-r26b-linux.zip
具体各位读者可以参考下面这篇博客:https://blog.csdn.net/qq_33979657/article/details/125187813
mk实现ndk交叉编译——了解即可
将工程的Test.c和Login.c打进so中。
- 编写gradle脚本
plugins {
id 'com.android.application'
}
android {
namespace 'com.mvp.myapplication'
compileSdk 32
defaultConfig {
applicationId "com.mvp.myapplication"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//1.第一步
externalNativeBuild{
cmake{
abiFilters "armeabi-v7a"
}
}
ndk{
abiFilters "armeabi-v7a"
}
//=====================
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
//原始版本:手动写一个Android.mk文件,指定mk的路径
externalNativeBuild{
ndkBuild{
path 'src/main/ndkDir/Android.mk'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
- 编写mk文件
# 这里能够决定编译 Login.c Test.c
# 1.源文件所在的位置,宏函数 my-dir 返回当前目录(包含Android.mk 文件本身的目录)的路径
LOCAL_PATH := $(call my-dir)
$(info "LOCAL_PATH:======${LOCAL_PATH}") # src/main/ndkDir
# 2.清理
include $(CLEAR_VARS)
# TODO 预编译库的引入 == 提前编译好的库
LOCAL_MODULE := fmod
LOCAL_SRC_FILES := libfmod.so #引入动态库 .a 引入静态库
# 预编译动态库的makefile脚本
include $(PREBUILT_SHARED_LIBRARY)
# 预编译静态库的makefile脚本
# include $(PREBUILT_STATIC_LIBRARY)
# 引入其他makefile文件,之前最好都清理一遍,可以消除多余的LOCAL_XXX变量
include $(CLEAR_VARS)
# 3.指定库名称
# 存储需要构建的模块的名称,每个模块名称必须唯一,且不含任何空格
# 如果模块名称的开头已经是lib,则构建系统不会附加额外的前缀lib,而是按照模块名称,并添加so后最
LOCAL_MODULE := MyLoginJar #libMyLoginJar.so
# 包含要构建到模块中的c/c++源文件列表 以空格分开
LOCAL_SRC_FILES := Login.c \
Test.c
# 开始链接
# 静态库的链接
# LOCAL_STATIC_LIBRARIES := fmod
# 动态库的链接
LOCAL_SHARED_LIBRARIES := fmod
# 导入log库
LOCAL_LDLIBS := -lm -llog
# 4.动态库
# 构建动态库 最后生成总动态库 ---> libMyLoginJar.so
include $(PREBUILT_SHARED_LIBRARY)
# 构建静态库
# include $(PREBUILT_STATIC_LIBRARY)
# libfmod.so 先打入到fmod模块,然后fmod模块再打入到MyLoginJar模块中
- 在java中引入并调用c相关的方法
package com.mvp.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("fmod");//如果是动态库,需要先加载,让系统生成一个副本
System.loadLibrary("MyLoginJar");
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
Cmake实现ndk交叉编译——大家可以参考笔者之前的一篇博客:
JNI技术之语言变声实现,这篇博客里面有对cmake相关的参数进行了详细介绍。这里笔者主要记录下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.18.1)
# Declares and names the project.
project("myapplication")
# 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.
myapplication
# 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.
myapplication
# Links the target library to the log library
# included in the NDK.
${log-lib})
#cmake语法
#log信息的输出——message
#(无) = 重要信息
#STATUS = 非重要信息
#WARNING = cmake警告 代码会继续执行
#AUTHOR_WARNING = cmake警告(dev) 代码会继续执行
#SENO_ERROR = cmake错误 代码继续执行,但是会跳过生成的步骤
#FATAL_ERROR = cmake错误,终止所有处理过程
message("我是消息========>")
# cmake变量 cmake中所有变量都是string类型,可以使用set()或unset()命令来声明或移除一个变量
#声明变量:set(变量名,变量值)
set(test 666)
unset(test)
message("test = ${test}") #会取不到值,test被移除了
#列表
#声明列表:set(列表名 值1 值2 ...)或set(列表名 "值1;值2...")
set( list 1 2 3 4)
message("list is ${list}")
set(list2 "1;2;3;4;5")
message("list2 is ${list2}")
#条件命令
#true(1,ON,YES,TRUE,Y,非0的值)
#false(0,OFF,NO,FALSE,N,IGNORE)
set(if_tap OFF)
set(elseif_tap ON)
if (${if_tap})
message("if")
elseif(${elseif_tap})
message("elseif")
endif ()
#循环命令
#NOT a STREQUAL "xxx"(a不等于 xxx)
# break()命令可以跳出整个循环,continue命令可以继续当前循环
set(a "")
while(NOT a STREQUAL "xxx")
set(a "${a}x")
message(">>>>>>>a = ${a}")
endwhile()
foreach(item 1 2 3)
message("item = ${item}")
endforeach()
foreach(item2 RANGE 2)
message("item2 = ${item2}")
endforeach()
set( list 1 2 3 4)
foreach(item IN LISTS list)
message("list = ${item}")
endforeach()
#函数命令
function(num_method n1 n2 n3)
message("call num_method method")
message("n1 = ${n1}")
message("n2 = ${n2}")
message("n3 = ${n3}")
message("ARGC = ${ARGC}")
message("arg1 = ${ARGV0} arg2 = ${ARGV1} arg3 = ${ARGV2}")
message("all args = ${ARGV}")
endfunction()
num_method(100 200 300)