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)
相关推荐
天空中的野鸟43 分钟前
Android音频采集
android·音视频
小白也想学C2 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程2 小时前
初级数据结构——树
android·java·数据结构
闲暇部落4 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX6 小时前
Android 分区相关介绍
android
大白要努力!7 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle
Estar.Lee7 小时前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood7 小时前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-10 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen13 小时前
MTK Android12 user版本MtkLogger
android·framework