AndroidNDK开发之交叉编译

在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命令进行下载:

css 复制代码
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中。

  1. 编写gradle脚本
java 复制代码
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文件
css 复制代码
# 这里能够决定编译 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相关的方法
css 复制代码
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的相关语法。

java 复制代码
# 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)
相关推荐
MiyamuraMiyako21 分钟前
从 0 到发布:Gradle 插件双平台(MavenCentral + Plugin Portal)发布记录与避坑
android
NRatel1 小时前
Unity 游戏提升 Android TargetVersion 相关记录
android·游戏·unity·提升版本
叽哥3 小时前
Kotlin学习第 1 课:Kotlin 入门准备:搭建学习环境与认知基础
android·java·kotlin
风往哪边走4 小时前
创建自定义语音录制View
android·前端
用户2018792831674 小时前
事件分发之“官僚主义”?或“绕圈”的艺术
android
用户2018792831674 小时前
Android事件分发为何喜欢“兜圈子”?不做个“敞亮人”!
android
Kapaseker5 小时前
你一定会喜欢的 Compose 形变动画
android
QuZhengRong6 小时前
【数据库】Navicat 导入 Excel 数据乱码问题的解决方法
android·数据库·excel
zhangphil7 小时前
Android Coil3视频封面抽取封面帧存Disk缓存,Kotlin(2)
android·kotlin
程序员码歌13 小时前
【零代码AI编程实战】AI灯塔导航-总结篇
android·前端·后端