Linux下编译成静态库和动态库,引入到项目中


目录
  • 配置全局变量
  • 编译动态库和静态库:
    • 动态库编译
    • 静态库编译
  • AS mk 方式加载静态库和动态库
    • 配置build.gradle
    • 加载静态库方法
      • 将libget.a考入到项目中
      • 配置Android.mk
      • 新建native-lib.c
    • 加载动态库方法
      • 将libget.so考入到项目中
      • 配置Android.mk文件
        • 在>=6.0系统上会报错
      • 加载libget.so库
  • AS cmake方式加载静态库和动态库
    • 加载静态库方法
      • 配置CMakeLists.txt文件
        • 1.以add_library和set_target_properties的方式配置动态库
        • 2.以set方式配置动态库
      • 调用链接库的方法
    • 加载动态库方法
      • 配置CMakeLists.txt文件
        • 1.以add_library和set_target_properties的方式配置动态库
        • 2.以set方式配置动态库
        • 在>=6.0系统上会报错
      • 加载libget.so库

配置全局变量

先在/etc/profile文件中配置全局变量

#NDK相关配置信息
export NDK="/home/zuojie/android-ndk-r17c"

export NDK_GCC_x86="$NDK/toolchains/x86-4.9/prebuilt/linux-x86_64/bin/i686-linux-android-gcc"
export NDK_GCC_x64="$NDK/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-gcc"
export NDK_GCC_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
export NDK_GCC_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-gcc"

export NDK_CFIG_x86="--sysroot=$NDK/platforms/android-21/arch-x86 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/i686-linux-android"
export NDK_CFIG_x64="--sysroot=$NDK/platforms/android-21/arch-x86_64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/x86_64-linux-android"
export NDK_CFIG_arm="--sysroot=$NDK/platforms/android-21/arch-arm -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/arm-linux-androideabi"
#这个实验,不成功
# export NDK_CFIG_arm_64="--isysroot=$NDK/platforms/android-21/arch-arm64 -isystem $NDK/sysroot/usr/include -isystem -isystem $NDK/sysroot/usr/include/aarch64-linux-android"
export NDK_CFIG_arm_64="--sysroot=$NDK/platforms/android-21/arch-arm64 -isystem $NDK/sysroot/usr/include -isystem $NDK/sysroot/usr/include/aarch64-linux-android"

export NDK_AR_x86="$NDK/toolchains/x86-4.9/prebuilt/linux-x86_64/bin/i686-linux-android-ar"
export NDK_AR_x64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"
export NDK_AR_arm="$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-ar"
export NDK_AR_arm_64="$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-ar"

# 静态库 动态库相关
export myd="$NDK_GCC_arm $NDK_CFIG_arm -fPIC -shared "
export myj="$NDK_AR_arm rcs "

编译动态库和静态库:

在Linux下使用NDK来编译动态库和静态库

动态库编译

$NDK_GCC_arm $NDK_CFIG_arm -fPIC -shared get.c -o libget.so

静态库编译

ndk编译静态库: 必须使用 arm-linux-androideabi-ar

必须先用交叉编译打出来的 .o,不能使用gcc

$NDK_GCC_arm $NDK_CFIG_arm -fPIC -c get.c -o get.o

在使用arm-linux-androideabi-ar打出静态库

$NDK_AR_arm rcs -o  libget.a *.o

AS mk 方式加载静态库和动态库

先新建一个普通的一个Android项目

配置build.gradle

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
    buildToolsVersion "29.0.0"
    defaultConfig {
        applicationId "com.zxj.ndk_mk"
        minSdkVersion 14
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        //指导源文件编译
        externalNativeBuild{
            ndkBuild{
                abiFilters "armeabi-v7a"
            }
        }

        //应该打包几种cpu
        ndk{
            abiFilters "armeabi-v7a"
        }
    }

    //配置Native的构建脚本路径
    externalNativeBuild{
        ndkBuild{
            path "src/main/cpp/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.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}

加载静态库方法

将libget.a考入到项目中

配置Android.mk

新建一个Android.mk文件

#源文件在的位置。宏函数 my-dir 返回当前目录(包含 Android.mk 文件本身的目录)的路径。
LOCAL_PATH := $(call my-dir)

# 打印
$(info "LOCAL_PATH :== $(LOCAL_PATH)")

#引入其他makefile文件。CLEAR_VARS 变量指向特殊 GNU Makefile,可为您清除许多 LOCAL_XXX 变量
#不会清理 LOCAL_PATH 变量
include $(CLEAR_VARS)

# TODO start 预编译库的引入 == 提前编译好的库
LOCAL_MODULE := myGet
LOCAL_SRC_FILES := libget.a
# LOCAL_SRC_FILES := libget.so
# 告诉构建工具是静态库
include ${PREBUILT_STATIC_LIBRARY}
# 告诉构建工具是动态库
# include ${PREBUILT_SHARED_LIBRARY}

#开始清理
include $(CLEAR_VARS)
#TODO end

#存储您要构建的模块的名称 每个模块名称必须唯一,且不含任何空格
#如果模块名称的开头已是 lib,则构建系统不会附加额外的前缀 lib;而是按原样采用模块名称,并添加 .so 扩展名。
LOCAL_MODULE := native-lib

#包含要构建到模块中的 C 和/或 C++ 源文件列表 以空格分开
LOCAL_SRC_FILES := native-lib.c \
Test.c


# TODO start 开始连接进来,将libget.a/libget.so连接到总库中(libnative-lib.so)
# 静态库的链接
LOCAL_STATIC_LIBRARIES := myGet
# 动态库的链接
# LOCAL_SHARED_LIBRARIES := myGet
# TODO end

#导入log
# LOCAL_LDLIBS := -llog
 LOCAL_LDLIBS := -lm -llog

#构建动态库
include $(BUILD_SHARED_LIBRARY)

新建native-lib.c

在MainActivity中声明getMyLibMethod方法

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        getMyLibMethod();
    }

    public native void getMyLibMethod();
}

使用javah生成头文件

javah com.zxj.ndk_mk.MainActivity

在native-lib.c中实现头文件

#include 
#include 

extern int get();

JNIEXPORT void JNICALL Java_com_zxj_ndk_1mk_MainActivity_getMyLibMethod
(JNIEnv *env, jobject inst){

    __android_log_print(ANDROID_LOG_DEBUG,"zuo","testgetMethod:%d",get());
}

加载动态库方法

加载动态库方法跟加载静态库基本一样,只不过有点小区别,下面是在加载静态库的基础上进行修改

将libget.so考入到项目中

配置Android.mk文件

#源文件在的位置。宏函数 my-dir 返回当前目录(包含 Android.mk 文件本身的目录)的路径。
LOCAL_PATH := $(call my-dir)

# 打印
$(info "LOCAL_PATH :== $(LOCAL_PATH)")

#引入其他makefile文件。CLEAR_VARS 变量指向特殊 GNU Makefile,可为您清除许多 LOCAL_XXX 变量
#不会清理 LOCAL_PATH 变量
include $(CLEAR_VARS)

# TODO start 预编译库的引入 == 提前编译好的库
LOCAL_MODULE := myGet
# LOCAL_SRC_FILES := libget.a
LOCAL_SRC_FILES := libget.so
# 告诉构建工具是静态库
# include ${PREBUILT_STATIC_LIBRARY}
# 告诉构建工具是动态库
include ${PREBUILT_SHARED_LIBRARY}

#开始清理
include $(CLEAR_VARS)
#TODO end

# TODO start 开始连接进来,将libget.a/libget.so连接到总库中(libnative-lib.so)
# 静态库的链接
# LOCAL_STATIC_LIBRARIES := myGet
# 动态库的链接
LOCAL_SHARED_LIBRARIES := myGet
# TODO end

#存储您要构建的模块的名称 每个模块名称必须唯一,且不含任何空格
#如果模块名称的开头已是 lib,则构建系统不会附加额外的前缀 lib;而是按原样采用模块名称,并添加 .so 扩展名。
LOCAL_MODULE := native-lib

#包含要构建到模块中的 C 和/或 C++ 源文件列表 以空格分开
LOCAL_SRC_FILES := native-lib.c \
Test.c

#导入log
# LOCAL_LDLIBS := -llog
 LOCAL_LDLIBS := -lm -llog

#构建动态库
include $(BUILD_SHARED_LIBRARY)
在>=6.0系统上会报错

加载动态库方法在>=6.0设备中是无法执行的

这个错误是报动态库的路径无法找到,而这个动态库的路径是电脑的路径,在Android设备上肯定是找不到的所以会报错。目前还没有找到在>=6.0设备的解决方法,最好是使用CMake

加载libget.so库

因为动态库在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,所以需要在加载总库(libnative-lib.so)之前进行加载libget.so库。

static {
    //一定要在加载总库(libnative-lib.so)之前进行加载libget.so库
    System.loadLibrary("get");
    System.loadLibrary("native-lib");
}

其他的跟加载静态库的代码一样,直接运行就可以成功

AS cmake方式加载静态库和动态库

先创建一个Native C++工程,创建好工程后,build.gradle文件里的externalNativeBuild里默认是cppFlags ""表示默认包含四大平台

我们可以使用abiFilters来指定某个平台

加载静态库方法

将静态库考入到某一个目录下,我这里考入的是cpp目录下

配置CMakeLists.txt文件

配置CMakeLists.txt有两种方式,

  1. 以add_library和set_target_properties的方式配置静态库
  2. 以set方式配置动态库
1.以add_library和set_target_properties的方式配置动态库

这里以add_library可以取一个名称MyGet,那么在下面就需要使用这个名称MyGet

cmake_minimum_required(VERSION 3.4.1)

#打印日志
message("当前CMake的路径时:${CMAKE_SOURCE_DIR}")

add_library(
        native-lib
        SHARED
        native-lib.cpp)


# 加入一个库,取名为MyGet,STATIC 静态库,IMPORTED 以导入的方式
add_library(MyGet STATIC IMPORTED)
#开始真正的导入
set_target_properties(MyGet PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libget.a)

find_library(
        log-lib
        log)

#链接到库
target_link_libraries(
        native-lib
        MyGet
        ${log-lib})
2.以set方式配置动态库

使用set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")这个进行配置。另外target_link_libraries里要配置的是libget.soget

cmake_minimum_required(VERSION 3.4.1)

#打印日志
message("当前CMake的路径时:${CMAKE_SOURCE_DIR}")

add_library(
        native-lib
        SHARED
        native-lib.cpp)


# 设置一个变量
# CMAKE_CXX_FLAGS C++的参数,会传给编译器。CMAKE_C_FLAGS C的参数,会传给编译器
# ${CMAKE_CXX_FLAGS} 使用变量,就是例如之前已经定了CMAKE_CXX_FLAGS= --sysroot=XX,现在重新定义 CMAKE_CXX_FLAGS 变量
# -L 指定库的路径
# CMAKE_SOURCE_DIR的值就是当前这个文件(CMakeLists.txt)的路径
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}")

find_library(
        log-lib
        log)

#链接到库
target_link_libraries(
        native-lib
        get
        ${log-lib})

调用链接库的方法

native-lib.cpp

#include 
#include 
#include 

//因为链接的库libget.a,是C语言的,这里是C++的,所以需要extern "C"
extern "C"{
    int get();
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_zxj_ndk_1cmake_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++" ;

    __android_log_print(ANDROID_LOG_DEBUG,"zuo","使用CMake方式---test getMethod:%d",get());

    return env->NewStringUTF(hello.c_str());
}

最后运行就可以了

加载动态库方法

将动态库考入到某一个目录下,这里我们习惯把动态库夸人到/main/jniLibs/armeabi-v7下

配置CMakeLists.txt文件

配置CMakeLists.txt有两种方式,

  1. 以add_library和set_target_properties的方式配置动态库
  2. 以set方式配置动态库
1.以add_library和set_target_properties的方式配置动态库

这里以add_library可以取一个名称MyGet,那么在下面就需要使用这个名称MyGet

cmake_minimum_required(VERSION 3.4.1)

#打印日志
message("当前CMake的路径时:${CMAKE_SOURCE_DIR}")
message("当前系统的平台:${CMAKE_ANDROID_ARCH_ABI}")

add_library(
        native-lib
        SHARED
        native-lib.cpp)


# 加入一个库,取名为MyGet,STATIC 静态库,IMPORTED 以导入的方式
add_library(MyGet SHARED IMPORTED)
# ${CMAKE_ANDROID_ARCH_ABI} 当前系统的平台,这里就是armeabi-v7a
set_target_properties(MyGet PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libget.so)

find_library(
        log-lib
        log)

#链接到库
target_link_libraries(
        native-lib
        MyGet
        ${log-lib})
2.以set方式配置动态库

使用set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")这个进行配置。另外target_link_libraries里要配置的是libget.soget

cmake_minimum_required(VERSION 3.4.1)

#打印日志
message("当前CMake的路径时:${CMAKE_SOURCE_DIR}")
message("当前系统的平台:${CMAKE_ANDROID_ARCH_ABI}")

add_library(
        native-lib
        SHARED
        native-lib.cpp)

# 设置一个变量
# CMAKE_CXX_FLAGS C++的参数,会传给编译器。CMAKE_C_FLAGS C的参数,会传给编译器
# ${CMAKE_CXX_FLAGS} 使用变量,就是例如之前已经定了CMAKE_CXX_FLAGS= --sysroot=XX,现在重新定义 CMAKE_CXX_FLAGS 变量
# -L 指定库的路径
# CMAKE_SOURCE_DIR的值就是当前这个文件(CMakeLists.txt)的路径
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")

find_library(
        log-lib
        log)

#链接到库
target_link_libraries(
        native-lib
        get
        ${log-lib})
在>=6.0系统上会报错

CMake加载动态库方式跟Makefile的一样,第一种方式只能在6.0以下使用,否则会报错,原因跟Makefile的一样,而第2种以set方式的可以兼容>=6.0版本以上的

加载libget.so库

因为动态库在编译链接时并没有把库文件的代码加入到可执行文件中,而是在程序执行时由运行时链接文件加载库,所以需要在加载总库(libnative-lib.so)之前进行加载libget.so库。

static {
    System.loadLibrary("get");
    System.loadLibrary("native-lib");
}
NDK