美文网首页Android-NDK/JNINDK
NDK开发必知必会3⃣️MakeFile详解

NDK开发必知必会3⃣️MakeFile详解

作者: 289346a467da | 来源:发表于2018-11-01 00:02 被阅读15次

    前言

    Android.mk 的文件配置详解,能够读懂Android.mk;为何Google推荐使用cmake,而不在使用Android.mk?Android.mk存在哪些缺陷?

    如何生成静态库与动态库(在上几篇文章中讲过)?

    Android.mk 如何配置动态库(.so文件)、配置静态库(.a文件)?

    静态库与动态库的区别?

    带着这些问题,思考,往下看。

    环境:

    AS 3.1.0 版本

    NDK 16 版本

    命令行: Mac 自带的命令行

    通过命令来生成动态库和静态库

    这里用mac的终端来演示,通过vim 命令来生成一个.c文件,这个就不必细说了吧。在NDK 开发必知必会1⃣️CC++编译器配置,中有详细的讲解

    输入: vim a.c

    image.png

    然后配置一个临时的变量:

    export CC="/Users/prim/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-gcc --sysroot=/Users/prim/Library/Android/sdk/ndk-bundle/platforms/android-21/arch-arm -isystem /Users/prim/Library/Android/sdk/ndk-bundle/sysroot/usr/include -isystem /Users/prim/Library/Android/sdk/ndk-bundle/sysroot/usr/include/arm-linux-androideabi"
    

    输入 :echo $CC 验证变量是否配置成功

    输入 :$CC -fPIC -shared a.c -o libA.so 生成libA.so文件,这里便生成了一个动态库

    输入:$CC -fPIC -c a.c -o a.o 生成a.o 文件,用于生成静态库

    输入 : /Users/prim/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-ar r libA.a a.o

    会生成libA.a 静态库文件。

    将得到的libA.so libA.a copy到项目的cpp 文件夹下(cpp可参照如何配置NDK开发环境)。

    配置依赖动态库

    NDK 环境的配置,这里就不再细说了,配置完成如下图:


    image.png

    gradle 配置

    image.png

    下面是重点,Android.mk文件如何配置动态库

    LOCAL_PATH := $(call my-dir)
    
    # 预编译库引入(提前编译好的库)
    include $(CLEAR_VARS)
    LOCAL_MODULE := A
    LOCAL_SRC_FILES := libA.so
    # 构建动态库
    include $(PREBUILT_SHARED_LIBRARY)
    
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := holle-jni
    LOCAL_SRC_FILES := holle-jni.c
    # 编译holle-jni模块 需要连接A模块
    # A模块是一个预编译库模块 动态库
    LOCAL_SHARED_LIBRARIES := A
    # 动态库配置
    include $(BUILD_SHARED_LIBRARY)
    

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

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

    LOCAL_MODULE := hello-jni:存储您要构建的模块的名称 每个模块名称必须唯一,且不含任何空格

    如果模块名称的开头已是 lib,则构建系统不会附加额外的前缀 lib;而是按原样采用模块名称,并添加 .so 扩展名。

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

    构建动态库 include $(BUILD_SHARED_LIBRARY)

    这样便配置好了动态库,build一下看cpp文件有没有变亮,如果变亮则动态库配置成功。

    会生成两个so包libholle-jni.so libA.so

    image.png

    如何使用libA.so中的函数呢?

    编辑 holle-jni.c

    #include <jni.h>
    //libA.so 中的方法
    extern int test1();
    
    
    
    JNIEXPORT void JNICALL
    Java_ndk_config_com_configndk_MainActivity_useLibAMoudle(JNIEnv *env, jobject instance) {
    
        // TODO
        test1();
    }
    

    Activity 中代码

    static {
            System.loadLibrary("holle-jni");
        }
    

    这样就可以调用动态库中的函数了。

    Android.mk 配置动态库的缺陷

    在4.4上 如果load一个动态库 ,需要先将这个动态库的依赖的其他动态库load进来
    比如:在Android 4.4 先要load holle-jni 链接的动态库,要在load holle-jni 之前load进来

    System.loadLibrary("A");
     System.loadLibrary("holle-jni");
    

    从6.0开始 使用Android.mk 如果来引入一个预编译动态库 有问题
    在6.0以下 System.loadLibrary 不会自动为我们加载依赖的动态库
    6.0以上 System.loadLibrary 会自动为我们加载依赖的动态库

    那么在6.0 以上就不能使用,如下方法loadlibary,否则项目会报错:

    System.loadLibrary("A");
     System.loadLibrary("holle-jni");
    

    改成,因为6.0以上会自动加载依赖的动态库。

     System.loadLibrary("holle-jni");
    

    这里并没有太好的解决方法,或许是因为这个原因,Google才推荐使用cmake的方式,现在的ndk对mk的支持已经接近放弃阶段了。

    配置依赖静态库

    LOCAL_PATH := $(call my-dir)
    
    # 预编译库引入(提前编译好的库)
    include $(CLEAR_VARS)
    LOCAL_MODULE := A
    LOCAL_SRC_FILES := libA.a
    # 构建静态库
    include $(PREBUILT_STATIC_LIBRARY)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := holle-jni
    LOCAL_SRC_FILES := holle-jni.c
    # 构建静态库
    LOCAL_STATIC_LIBRARIES := A
    
    # 动态库配置
    include $(BUILD_SHARED_LIBRARY)
    

    调用静态库的函数和调用动态库的函数方式一致。

    同时Activity只需要,便可以,静态库不需要动态的加载依赖,在打包时已经将静态库打进去了。

     System.loadLibrary("holle-jni");
    

    使用静态库会生成一个so文件,很显然apk会小很多

    image.png

    静态库与动态库的区别

    静态库节省时间:不需要再进行动态链接,需要调用的代码直接就在代码内部

    动态库节省空间:如果一个动态库被两个程序调用,那么这个动态库只需要在内存中

    Java中在不经过封装的情况下只能直接使用动态库。

    我们可以这样比喻两个库的区别:
    jar包 =》 a.java b.java c.java

    app: app.java(a.java),app.java 调用jar包的 a.java
    假设jar包是静态库,那么打包的apk 只包含 app.java + a.java 而没有将 b.java c. java 打进apk包中。
    app.apk --> app.java+a.java

    假设jar包是动态库,直接将整个jar包打入到apk包中。
    app.apk: app.java+ jar包(a.java b.java c.java)

    显然,如果我们使用静态库(.a)打的apk包要比动态库(.so)打的apk包小很多。

    MakeFile 配置详解

    变量和宏

    定义自己的任意变量。在定义变量时请注意,NDK 构建系统会预留以下变量名称:

    以 LOCAL_ 开头的名称,例如 LOCAL_MODULE。
    以 PRIVATE_、NDK_ 或 APP 开头的名称。构建系统在内部使用这些变量。
    小写名称,例如 my-dir。构建系统也是在内部使用这些变量。
    如果为了方便而需要在 Android.mk 文件中定义自己的变量,建议在名称前附加 MY_。

    常用内置变量

    变量名 | 含义 | 示例
    --|--|--|--
    BUILD_STATIC_LIBRARY | 构建静态库的Makefile脚本 | include (BUILD_STATIC_LIBRARY) PREBUILT_SHARED_LIBRARY | 预编译共享库的Makeifle脚本 | include(PREBUILT_SHARED_LIBRARY)
    PREBUILT_STATIC_LIBRARY | 预编译静态库的Makeifle脚本 | include $(PREBUILT_STATIC_LIBRARY)
    TARGET_PLATFORM Android API | 级别号 | TARGET_PLATFORM := android-22
    TARGET_ARCH | CUP架构 | arm arm64 x86 x86_64
    TARGET_ARCH_ABI | CPU架构 | armeabi armeabi-v7a arm64-v8a

    模块描述变量

    变量名 | 描述 | 例
    --|--|--|--
    LOCAL_MODULE_FILENAME | 覆盖构建系统默认用于其生成的文件的名称 | LOCAL_MODULE := foo LOCAL_MODULE_FILENAME := libnewfoo
    LOCAL_CPP_FEATURES | 特定 C++ 功能 | 支持异常:LOCAL_CPP_FEATURES := exceptions
    LOCAL_C_INCLUDES | 头文件目录查找路径 | LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
    LOCAL_CFLAGS | 构建 C 和 C++ 的编译参数 | LOCAL_CPPFLAGS c++
    LOCAL_STATIC_LIBRARIES | 当前模块依赖的静态库模块列表 | LOCAL_SHARED_LIBRARIES
    LOCAL_WHOLE_STATIC_LIBRARIES | --whole-archive | 将未使用的函数符号也加入编译进入这个模块
    LOCAL_LDLIBS | 依赖 系统库 | LOCAL_LDLIBS := -lz

    导出给引入模块的模块使用:

    LOCAL_EXPORT_CFLAGS

    LOCAL_EXPORT_CPPFLAGS

    LOCAL_EXPORT_C_INCLUDES

    LOCAL_EXPORT_LDLIBS

    引入其他模块

    #将一个新的路径加入NDK_MODULE_PATH变量
    #NDK_MODULE_PATH 变量是系统环境变量
    $(call import-add-path,$(LOCAL_PATH)/platform/third_party/android/prebuilt)
    #包含CocosDenshion/android目录下的mk文件
    $(call import-module,CocosDenshion/android)
    
    #这里即为 我需要引入 CocosDenshion/android 下面的Android.mk
    #CocosDenshion/android 的路径会从 $(LOCAL_PATH)/platform/third_party/android/prebuilt 去查找
    

    相关文章

      网友评论

        本文标题:NDK开发必知必会3⃣️MakeFile详解

        本文链接:https://www.haomeiwen.com/subject/ccxtxqtx.html