• Android.mk走读与Cmake配置


    Android.mk认识:

    在上一次【https://www.cnblogs.com/webor2006/p/9946061.html】中学会了用NDK提供的交叉编译工程编译成Android能运行的可执行文件,下面咱们来做个实验来看一下使用静态库与动态库的区别,还是用上一次用的源文件为例:

    动态库的具体的生成过程可以参考上一次写的博文,接下来再生成一个静态库,如何生成呢?

    所以咱们先来找到NDK提供的ar交叉编译工具:

    所以咱们使用它依照生成规则来生成对应的静态库看一下:

    接下来咱们新建一个Android工程,来使用编译出来的动静态库,这里还是建立一个纯的非NDK的Android工程,来进一步操练下如何给一个普通Android工程来集成NDK,如下:

    然后咱们来集成ndk环境,回顾上一次的步骤,得修改app下的build.gradle配置了:

    这个是针对源文件的,而我们想要用Android.mk来进行构建就需要在外层来指定了,如下:

     

    其实上一次已经使用过了,里面Android.mk的编写规则也记不住,直接拷贝一下,重点不是记着,还是你能读懂就成,如下:

    然后咱们在对应位置建立一个native-lib.c的源文件:

    那咱们编译一个apk,然后可以看到apk中已经有一个.so文件了,如下:

    那如果想使用我们刚编译写的libTest.so文件应该怎么使用呢?这里就涉及到多模块的引入问题,先将我们编译好的libTest.so拷入到工程中:

    然后在Android.mk中来声明一个新的模块,其写法跟声明.c源文件类似,如下:

    然后咱们再来编译一个apk,看一下里面的.so的情况,是不是变为2个.so了?

    那是因为咱们新声明的预编译模块还没有使用到,所以需要关联一下,如下:

    那此时咱们来在自己的.c文件中来使用一下预编译库定义的函数,如下:

    当然在运行之前还得加载一个我们编译的库:

    然后编译生成一个apk,看此时它里面包含动态库的情况:

    记住动态库的这个现象哈~~之后会用静态库做一个对比!!!!

    然后下面分别在Android4.3系统和Android8.1.0的系统上运行,为啥要在两个版本上运行,因为肯定是有坑嘛~~,所以下面先来看在Android4.3上运行,用的手机型号为:

    崩溃了,看一下崩溃日志:

    11-27 09:12:04.756 6028-6028/com.jni.test E/AndroidRuntime: FATAL EXCEPTION: main
        java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libTest.so" needed by "libnative-lib.so"; caused by library "/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libTest.so" not found
            at java.lang.Runtime.loadLibrary(Runtime.java:362)
            at java.lang.System.loadLibrary(System.java:525)
            at com.jni.test.MainActivity.<clinit>(MainActivity.java:9)
            at java.lang.Class.newInstanceImpl(Native Method)
            at java.lang.Class.newInstance(Class.java:1130)
            at android.app.Instrumentation.newActivity(Instrumentation.java:1078)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2223)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2362)
            at android.app.ActivityThread.access$700(ActivityThread.java:168)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1329)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:177)
            at android.app.ActivityThread.main(ActivityThread.java:5493)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
            at dalvik.system.NativeStart.main(Native Method)

    找不到libTest.so,这是因为在Android6.0以下版本,System.loadLibrary()不会自动为我们加载依赖的动态库,如果load一个动态库,则需要先将这个动态库的依赖的其他动态库也要load进来,所以:

    那此时同样的代码在Android8.1.0的手机上来运行,手机型号是:

    又崩溃了,崩溃日志:

        --------- beginning of crash
    11-27 09:26:34.879 15598-15598/? E/AndroidRuntime: FATAL EXCEPTION: main
        Process: com.jni.test, PID: 15598
        java.lang.UnsatisfiedLinkError: dlopen failed: library "/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/ndkBuild/debug/obj/local/armeabi-v7a/libTest.so" not found
            at java.lang.Runtime.loadLibrary0(Runtime.java:1016)
            at java.lang.System.loadLibrary(System.java:1660)
            at com.jni.test.MainActivity.<clinit>(MainActivity.java:10)
            at java.lang.Class.newInstance(Native Method)
            at android.app.Instrumentation.newActivity(Instrumentation.java:1179)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3054)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3278)
            at android.app.ActivityThread.-wrap12(Unknown Source:0)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1894)
            at android.os.Handler.dispatchMessage(Handler.java:109)
            at android.os.Looper.loop(Looper.java:166)
            at android.app.ActivityThread.main(ActivityThread.java:7377)
            at java.lang.reflect.Method.invoke(Native Method)
            at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:469)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:963)

    标红处的地址很显示是我的mac电脑上的路径了,在手机上肯定找不到嘛,那为啥在8.1.0的手机上运行会出现从这个路径去加载预编译的动态库呢?其实这就是版本兼容的问题了,在NDK中提供了一个可以看到.so文件的依赖情况,如下:

    那咱们来查看一下我们的libnative-lib.so它里面的依赖情况,先定位到这个生成的.so文件的路径处:

    然后使用这个ndk的依赖查看命令:

    也正好是崩溃日志中所看到的路径,其实是从Android6.0开始,使用Android.mk如果来引入一个预编译动态库是有问题的【这个需要特别的注意!】,其原因也就是“Android6.0以下版本,System.loadLibrary()不会自动为我们加载依赖的动态库,而6.0及以上的版本是会自动为我们加载依赖的动态库”,也就是说在Android6.0以上版本不用我们显示的去写加载的预编译库了,也就是这句话在Android6.0上可以省略掉了:

    那使用预编译的动态库在Android6.0以上系统有问题,那如果是改用静态库会不会有问题呢?咱们试试,将之前我们编译好的静态库也放到工程当中:

    此时需要修改.mk文件了,如下:

    其中关于MK中的这些变量的使用可以参考:

    常用内置变量

    变量名含义示例
    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

    编译下编译生成一个apk:

    也就是说会将我们程序用到的libTest.a静态库中的那段代码抽离打包到我们自己的so当中,而动态库是整个会打包进apk,跟我们自己的so是独立的,然后在运行时进行依赖调用,也就是说其实使用静态做为预编译库来使用的话打出来的.so会小很多,因为只有我们使用到的才会打包,而动态库是整个都会打包,不管使用了还是木有使用。举个形象的例子来理解静态库与动态库打包的区别:

    假设有个jar里面包含a.java、b.java、c.java,然后咱们app有一个app.java,它使用了jar包中的a.java,如果使用了静态库打包,则最终apk中只包含app.java和a.java;而如果使用了动态库打包,则最终APK中包含app.java和整个jar(a.java、b.java、c.java),很显示在开发中一般使用静态库较好,但是有个问题:为啥三方SDK一般都是提供.so文件呢?其实根本原因还是之前讲的:它只能加载动态库,不加载静态库:

    只是说如果是我们自己编译的库可以使用静态库,因为我们有NDK的编译环境嘛,如果三方给一个.a静态库,那我们在工程中还是配置NDK编译环境来将它进行一个封装,最终编成我们的.so再来使用,太麻烦了。 

    此时再运行,在不同版本上都可以兼容了。总之需要记住在mk中引入预编译的动态库是有版本性的差异滴,要解决的话就得用Cmake来代替.mk了,像如今的项目基本都是采用Cmake来进行NDK的编译了,那学习.mk有啥意义呢?因为好多老工程的编译还是基于.mk的,我们需要能看懂它,所以基于这个学习目的,下面来看一个cocos2d的一个hello world级别的工程,它里面的工程编译就是通过mk的形式,我们来试着读一下它的mk,看能否读懂,能读懂的话那目的就达到了:

    首先当然先导入这个cocos2d的工程啦,具体工程的代码可以上网上搜搜,这里导它的目的只是为了去了解它的mk文件的编写规则,并非研究怎么用它来开发游戏,如下:

    然后工程运行的界面如下:

    咱们重点关心的就是它的mk文件的编写内容啦,所在的位置如下:

    首先来看一下“Android.mk”完整配置:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    
    #模块名  如果没有 LOCAL_MODULE_FILENAME 配置 就会生成一个
    #libMyGame_shared.so
    LOCAL_MODULE := MyGame_shared
    
    #生成一个 libMyGame.so (可以不写)
    LOCAL_MODULE_FILENAME := libMyGame
    
    #源文件
    LOCAL_SRC_FILES := $(LOCAL_PATH)/hellocpp/main.cpp 
                       $(LOCAL_PATH)/../../../Classes/AppDelegate.cpp 
                       $(LOCAL_PATH)/../../../Classes/HelloWorldScene.cpp
    
    # 编译时查找头文件的路径 相当于:-I
    LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../Classes
    
    # _COCOS_HEADER_ANDROID_BEGIN
    # _COCOS_HEADER_ANDROID_END
    
    # 引入一个静态库作为当前编译源文件的依赖(jar)
    # 定义在其他的mk文件里面
    LOCAL_STATIC_LIBRARIES := cocos2dx_static
    
    # _COCOS_LIB_ANDROID_BEGIN
    # _COCOS_LIB_ANDROID_END
    
    include $(BUILD_SHARED_LIBRARY)
    
    # 单独声明是没有任何意义的 需要结合 import-module 来看
    
    # 设置引入其他mk的查找路径
    $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d)
    $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/external)
    $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos)
    $(call import-add-path,$(LOCAL_PATH)/../../../cocos2d/cocos/audio/include)
    #引入其他路径下的Android.mk文件
    # 相当于 #include
    $(call import-module, cocos)
    
    
    
    # _COCOS_LIB_IMPORT_ANDROID_BEGIN
    # _COCOS_LIB_IMPORT_ANDROID_END

    接下来就是尝试来读懂这里面的规则,第一句不用多说,每个Android.mk文件的开头的固定写法:

    可以瞅一下咱们生成的apk中的.so是否是我们指定的这个:

    嗯,确实是,继续往下看:

    对就是对应于这些源文件:

    关于-I参数的含义可以回顾一下:

    这个其实咱们在之前的DEMO中已经用到过了,如下:

    所以我们查找一下"cocos2dx_static"这个模块的定义,发现木有在当前的Android.mk中定义,而是在其它mk中,这就涉及到如何引入其它mk的module啦,具体使用如下:

    比较抽像对吧,来回到cocos2d这块的配置就能理解滴:

    对应于工程的这块:

    然后咱们打开它,看一下"cocos2dx_static"这个模块是否定义在这个.mk里面:

    至此对于第一个Android.mk就分析完了,貌似也都能看懂嘛,接下来还有另外一种mk,也就是下面要学习滴。

    Application.mk:

    同样是GNU Makefile 片段,在Application.mk中定义一些全局(整个项目)的配置

    先来瞅一下工程中的这个mk:

    先来看一下它里面都配了啥:

    # 指定运行时库 (libc )
    APP_STL := c++_static
    #会交给编译器的参数
    APP_CPPFLAGS := -frtti -DCC_ENABLE_CHIPMUNK_INTEGRATION=1 -std=c++11 -fsigned-char -Wno-extern-c-compat
    # 交给链接器的参数 (so依赖另一个so,这就需要链接 )
    APP_LDFLAGS := -latomic
    # 要生成的cpu架构
    APP_ABI := armeabi-v7a
    #解决windows命令行不支持太长的字符输入的问题
    APP_SHORT_COMMANDS := true
    
    
                          ifeq ($(NDK_DEBUG),1)
                            APP_CPPFLAGS += -DCOCOS2D_DEBUG=1
                            APP_OPTIM := debug
                          else
                            APP_CPPFLAGS += -DNDEBUG
                            APP_OPTIM := release
    endif

    接下来一个个了解一下:

    也就是我们在写cpp里的一些系统函数,如std::cout之类的,具体这块的配置参数如下:

    而它的定义如下:

    由于这个工程的源代码是基于cpp的,所以用上面这个标志,如果是c,则需要用下面这个:

    【注意】:由于NDK对于mk已经接近放弃的阶段,所以对于CPU的指定建议还是要build.gradle中去指定,如下:

    而它的具体配置如下:

    需要生成的cpu架构(ndk r17 只支持:armeabi-v7a, arm64-v8a, x86, x86_64)

    指令集
    基于 ARMv7 的设备上的硬件 FPU 指令 APP_ABI := armeabi-v7a
    ARMv8 AArch64 APP_ABI := arm64-v8a
    IA-32 APP_ABI := x86
    Intel64 APP_ABI := x86_64
    MIPS32 APP_ABI := mips
    MIPS64 (r6) APP_ABI := mips64
    所有支持的指令集 APP_ABI := all

    不同 Android 手机使用不同的 CPU,因此支持不同的指令集。

    armeabi-v7a

    armeabi-v7a ABI 使用 -mfloat-abi=softfp 开关强制实施规则,要求编译器在函数调用时必须传递核心寄存器对中的所有双精度值,而不是专用浮点值。 系统可以使用 FP 寄存器执行所有内部计算。 这样可极大地加速计算。

    如果要以 armeabi-v7a ABI 为目标,则必须设置下列标志:

    CFLAGS= -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16
    

    arm64-v8a

    此 ABI 适用于基于 ARMv8、支持 AArch64 的 CPU。它还包含 NEON 和 VFPv4 指令集。

    x86

    此 ABI 适用于支持通常称为“x86”或“IA-32”的指令集的 CPU。设置的标志如:

    -march=i686 -mtune=intel -mssse3 -mfpmath=sse -m32
    

    x86_64

    -march=x86-64 -msse4.2 -mpopcnt -m64 -mtune=intel
    

    现在手机主要是armeabi-v7a。查看手机cpu:

    adb shell cat /proc/cpuinfo

    adb shell getprop ro.product.cpu.abi

    如查看一下我模拟器的cpu情况:

    xiongweideMacBook-Pro:LableCoffee xiongwei$ adb shell cat /proc/cpuinfo
    processor    : 0
    vendor_id    : GenuineIntel
    cpu family    : 6
    model        : 69
    model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
    stepping    : 1
    cpu MHz        : 2600.058
    cache size    : 3072 KB
    physical id    : 0
    siblings    : 4
    core id        : 0
    cpu cores    : 4
    apicid        : 0
    initial apicid    : 0
    fpu        : yes
    fpu_exception    : yes
    cpuid level    : 13
    wp        : yes
    flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
    bugs        :
    bogomips    : 5200.11
    clflush size    : 64
    cache_alignment    : 64
    address sizes    : 39 bits physical, 48 bits virtual
    power management:
    
    processor    : 1
    vendor_id    : GenuineIntel
    cpu family    : 6
    model        : 69
    model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
    stepping    : 1
    cpu MHz        : 2600.058
    cache size    : 3072 KB
    physical id    : 0
    siblings    : 4
    core id        : 1
    cpu cores    : 4
    apicid        : 1
    initial apicid    : 1
    fpu        : yes
    fpu_exception    : yes
    cpuid level    : 13
    wp        : yes
    flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
    bugs        :
    bogomips    : 5200.11
    clflush size    : 64
    cache_alignment    : 64
    address sizes    : 39 bits physical, 48 bits virtual
    power management:
    
    processor    : 2
    vendor_id    : GenuineIntel
    cpu family    : 6
    model        : 69
    model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
    stepping    : 1
    cpu MHz        : 2600.058
    cache size    : 3072 KB
    physical id    : 0
    siblings    : 4
    core id        : 2
    cpu cores    : 4
    apicid        : 2
    initial apicid    : 2
    fpu        : yes
    fpu_exception    : yes
    cpuid level    : 13
    wp        : yes
    flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
    bugs        :
    bogomips    : 5200.11
    clflush size    : 64
    cache_alignment    : 64
    address sizes    : 39 bits physical, 48 bits virtual
    power management:
    
    processor    : 3
    vendor_id    : GenuineIntel
    cpu family    : 6
    model        : 69
    model name    : Intel(R) Core(TM) i5-4278U CPU @ 2.60GHz
    stepping    : 1
    cpu MHz        : 2600.058
    cache size    : 3072 KB
    physical id    : 0
    siblings    : 4
    core id        : 3
    cpu cores    : 4
    apicid        : 3
    initial apicid    : 3
    fpu        : yes
    fpu_exception    : yes
    cpuid level    : 13
    wp        : yes
    flags        : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ht syscall nx rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 movbe popcnt aes xsave avx rdrand lahf_lm abm
    bugs        :
    bogomips    : 5200.11
    clflush size    : 64
    cache_alignment    : 64
    address sizes    : 39 bits physical, 48 bits virtual
    power management:
    
    xiongweideMacBook-Pro:LableCoffee xiongwei$ adb shell getprop ro.product.cpu.abi
    x86

    apk在安装的时候,如果手机是armeabi-v7a的,则会首先查看apk中是否存在armeabi-v7a目录,如果没有就会查找armeabi。

    保证cpu目录下so数量一致。

    ​ 如果目标是armeabi-v7a,但是拥有一个armeabi的,也可以把它放到armeabi-v7a目录下。但是反过来不行

    ABI(横 so)/CPU(竖 手机)armeabiarmeabi-v7aarm64-v8ax86x86_64
    ARMV5 支持        
    ARMV7 支持 支持      
    ARMV8 支持 支持 支持    
    X86       支持  
    X86_64       支持 支持

    继续还是回到工程中的.mk往下瞅:

    其中APP_OPTIM的具体含义如下:

    以上就是关于mk的东东,配置比较复杂,没必要去记,因为真实项目使用可能都会转到下面要学习的cmake了,重点是要能读取mk,然后可以很容易的将mk转成cmake就可以啦~~

    Cmake配置:

    在android studio 2.2及以上,构建原生库的默认工具是 CMake。

    ​ CMake是一个跨平台的构建工具,可以用简单的语句来描述所有平台的安装(编译过程)。能够输出各种各样的makefile或者project文件。Cmake 并不直接建构出最终的软件,而是产生其他工具的脚本(如Makefile ),然后再依这个工具的构建方式使用

    ​ CMake是一个比make更高级的编译配置工具,它可以根据不同平台、不同的编译器,生成相应的Makefile或者vcproj项目。从而达到跨平台的目的。Android Studio利用CMake生成的是ninja,ninja是一个小型的关注速度的构建系统。我们不需要关心ninja的脚本,知道怎么配置cmake就可以了。从而可以看出cmake其实是一个跨平台的支持产出各种不同的构建脚本的一个工具。

    CMake的脚本名默认是CMakeLists.txt

    其中标红处提到了一个新名词“ninja”,其实它存在于我们的SDK当中,如下:

    当然我们不用关心ninja是如何构建的啦,只需了解CMake如何做就成了,这里做一个了解。

    下面咱们来基于之前学习mk的工程来改用cmake,工程回忆一下:

    首先得修改gradle的ndk编译脚本的路径,如下:

    然后在cmake里面也用之前的native-lib.c这个源文件:

    那接下来就是来在CMakeLists.txt中来编写构造脚本啦,那,如何写呢。。下面来学习下,首先得指定一下最低版本,如下:

    注意一个小细节:

    接下来指令要编译的源文件及编译出来是要静态库或动态库,如下:

    此时编译一下:

    再次编译:

    这里通过建立一个带NDK环境的工程的这块的配置一对比,发现它的ndk配置是这样写的:

    那咱们依葫芦画瓢一下:

    再編译一下:

    Build command failed.
    Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/mips64 --target native-lib}
    [1/1] Linking C shared library /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/cmake/debug/obj/mips64/libnative-lib.so
    FAILED: : && /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang  --target=mips64el-none-linux-android --gcc-toolchain=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/platforms/android-21/arch-mips64 -fPIC -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fno-limit-debug-info  -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/cmake/debug/obj/mips64/libnative-lib.so CMakeFiles/native-lib.dir/native-lib.c.o  -lm && :
    CMakeFiles/native-lib.dir/native-lib.c.o: In function `Java_com_jni_test_MainActivity_nativeTest':
    /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/cmake/native-lib.c:7: undefined reference to `test'
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    ninja: build stopped: subcommand failed.

    很显然在咱们的源代码中引用了动态库或静态库中的函数,但是我们在CMakeLists.txt中并未指定动态库或静态库的配置,所以接下来咱们先以引入静态库为例:

    那从哪里来导入呢,接下来需要设置一下静态库的路径:

    由于libTest.a跟我们的CMakeLists.txt不在同一个目录,所以路径需要注意一下,最后一步需要进行链接,也就是将这两个模块需要关联起来:

    如何写呢?如下:

    好,一切设置完毕,咱们试着来编译一下看是否正常了:

    Build command failed.
    Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/mips64 --target native-lib}
    ninja: error: '../../cpp/libTest.a', needed by '/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/build/intermediates/cmake/debug/obj/mips64/libnative-lib.so', missing and no known rule to make it

    貌似是静态库的路径这样设置不行,怎么办呢?其实可以尝试将CMakeLists.txt文件挪一下位置,咱们先来看一下目录结构:

    挪了位置之后,当然build.gradle的脚本配置那块也得变下了,如下:

    然后在CMakeLists.txt路径那可以修改为:

    然后再编译一下:

    Build command failed.
    Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/mips64 --target native-lib}
    [1/1] Linking C shared library ../../../../build/intermediates/cmake/debug/obj/mips64/libnative-lib.so
    FAILED: : && /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang  --target=mips64el-none-linux-android --gcc-toolchain=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/platforms/android-21/arch-mips64 -fPIC -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fno-limit-debug-info  -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ../../../../build/intermediates/cmake/debug/obj/mips64/libnative-lib.so CMakeFiles/native-lib.dir/src/main/cmake/native-lib.c.o  ../../../../src/main/cpp/libTest.a -lm && :
    /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/mips64el-linux-android-4.9/prebuilt/darwin-x86_64/lib/gcc/mips64el-linux-android/4.9.x/../../../../mips64el-linux-android/bin/ld: unknown architecture of input file `../../../../src/main/cpp/libTest.a(test.o)' is incompatible with mips:isa64r6 output
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    ninja: build stopped: subcommand failed.

    又失败了,有点崩溃。。貌似从标红处看是不支持相应的架构,此时想起来在build.gradle中打包APK时需要指定CPU类型,所以可以尝试加一下:

    再编译,终于成功了:

    其中需要注意这个:

    其实还有一个变量用得比较多,如下:

    编译看一下它的输出:

    那这个值是从哪获取的呢?其实就是我们在build.gradle中配置的,如下:

    如果咱们再加一个CPU架构,再看一下输出效果:

     

    上面是链接的静态库的配置,下面来使用动态库来链接,那又如何配呢?这里需要注意:需要将我们的动态.so库放到jniLibs里面,否则不会打包进apk,具体如下:

    然后修改CMakeLists.txt配置:

    此时,咱们在Android4.3机器上运行一下:

    11-30 09:42:22.583 5731-5731/? E/AndroidRuntime: FATAL EXCEPTION: main
        java.lang.UnsatisfiedLinkError: dlopen failed: could not load library "../../../../src/main/jniLibs/armeabi-v7a/libTest.so" needed by "libnative-lib.so"; caused by library "../../../../src/main/jniLibs/armeabi-v7a/libTest.so" not found
            at java.lang.Runtime.loadLibrary(Runtime.java:362)
            at java.lang.System.loadLibrary(System.java:525)
            at com.jni.test.MainActivity.<clinit>(MainActivity.java:9)
            at java.lang.Class.newInstanceImpl(Native Method)
            at java.lang.Class.newInstance(Class.java:1130)
            at android.app.Instrumentation.newActivity(Instrumentation.java:1078)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2223)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2362)
            at android.app.ActivityThread.access$700(ActivityThread.java:168)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1329)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:177)
            at android.app.ActivityThread.main(ActivityThread.java:5493)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:525)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
            at dalvik.system.NativeStart.main(Native Method)

    其实跟用.mk引入预编译动态库一样,6.0之前的系统是不会自动加入依赖库的,所以咱们需要手动加载一下依赖库,如下:

    此时再运行就正常了,然后apk中就有了两个so文件,如下:

    那如果改运行到Android6.0以上的机型,运行:

    但是和mk不同的是,CMake有其它办法引入动态库让它运行在Android6.0以上也没问题,如何整:

    试一下:

    再运行,在所有版本都可运行了。

    关于.mk和cmake引入预编译动态库在Android5.0及以下与6.0及以上的注意事项这里总结一下,以勉踩坑:​

    比如存在两个动态库libhello-jni.so 与 libTest.solibhello-jni.so依赖于libTest.so (使用NDK下的ndk-depends可查看依赖关系),则:

    其中对于Android.mk来说: 使用Android.mk在 >=6.0 设备上不能再使用预编译动态库(静态库没问题):

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE := Test
    #libTest.so放在当前文件同目录
    LOCAL_SRC_FILES := libTest.so
    #预编译库
    include $(PREBUILT_SHARED_LIBRARY)
    
    
    include $(CLEAR_VARS)
    #引入上面的Test模块
    LOCAL_SHARED_LIBRARIES := Test
    LOCAL_MODULE := hello-jni
    LOCAL_SRC_FILES := hello-jni.c
    include $(BUILD_SHARED_LIBRARY)

    上面这段配置生成的libhllo-jni在>=6.0设备中无法执行。

    而对于CMake来说:使用CMakeList.txt在 >=6.0 设备上引入预编译动态库配置如下:

    cmake_minimum_required(VERSION 3.4.1)
    
    file(GLOB SOURCE *.c )
    add_library(
                 hello-jni
                 SHARED
                ${SOURCE} )
    #这段配置在6.0依然没问题 
    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -L[SO所在目录]")
    
    #这段配置只能在6.0以下使用 原因和android.mk一样
    #add_library(Test SHARED IMPORTED)
    #set_target_properties(Test PROPERTIES IMPORTED_LOCATION [SO绝对地址])
    
    target_link_libraries(  hello-jni Test )

    好,接下来看另外一个CMake的东东:如果说我们的代码中要使用Android的Log库,之前咱们也说过,它的实现是处于NDK的这块位置:

    咱们来看一下在CMake中来如何指定这个动态库,先在源代码中增加一个LOG的代码:

    目前没有配置的话编译肯定会抛异常,如下:

    Build command failed.
    Error while executing process /Users/xiongwei/android-sdks/Android/sdk/cmake/3.6.3155560/bin/cmake with arguments {--build /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/.externalNativeBuild/cmake/debug/armeabi-v7a --target native-lib}
    [1/2] Building C object CMakeFiles/native-lib.dir/src/main/cmake/native-lib.c.o
    clang: warning: argument unused during compilation: '-L/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/jniLibs/armeabi-v7a'
    [2/2] Linking C shared library ../../../../build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so
    FAILED: : && /Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/bin/clang  --target=armv7-none-linux-androideabi --gcc-toolchain=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64 --sysroot=/Users/xiongwei/android-sdks/Android/sdk/ndk-bundle/platforms/android-14/arch-arm -fPIC -g -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -fno-integrated-as -mthumb -Wa,--noexecstack -Wformat -Werror=format-security  -L/Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/jniLibs/armeabi-v7a -O0 -fno-limit-debug-info  -Wl,--build-id -Wl,--warn-shared-textrel -Wl,--fatal-warnings -Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,-z,noexecstack -Qunused-arguments -Wl,-z,relro -Wl,-z,now -shared -Wl,-soname,libnative-lib.so -o ../../../../build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so CMakeFiles/native-lib.dir/src/main/cmake/native-lib.c.o  -lTest -lm && :
    /Users/xiongwei/Documents/workspace/studio/jnistudys/jnistudy1/app/src/main/cmake/native-lib.c:11: error: undefined reference to '__android_log_print'
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    ninja: build stopped: subcommand failed.

    下面来配置一下,这里就会学习到用查找的方式来将这个系统日志的so的路径给查出来,而不是手动去写死,如下:

    咱们来看一下打印:

    运行看一下是否打印出来日志了:

    但是!!其实上面在CMake引入log库的方式还可以简化,如下:

    另外还有一点就是咱们目前的源文件只有一个,这样配置没啥问题:

    那如果有很多源文件一个个这样添加是不是很麻烦,这时可以引用某个目录下的所有源文件,如下:

    另外还有一种指定目录的方式,了解下:

    如果在cmake中需要使用其他目录的cmakelist,可以这样,具体就不演示了:

    另外对于头文件的包含还有个小细节需要注意下,先新建一个头文件:

    如果源文件中想要引用这个头文件,可以这样写:

    但是还可以在CMake中加入这样一个配置来支持这样的写法,如下:

    具体配置如下:

    其实这句话配置就相当于:

    此时再编译源文件就没报红了:

    至此关于.mk和cmake相关配置的东东就学到这了,比上次学习还杂,但是收获还是颇多滴~~

  • 相关阅读:
    Beta冲刺
    Beta冲刺总结随笔
    用户使用调查报告
    Beta冲刺测试随笔
    WeChair项目Beta冲刺(10/10)
    WeChair项目Beta冲刺(9/10)
    WeChair项目Beta冲刺(8/10)
    WeChair项目Beta冲刺(7/10)
    WeChair项目Beta冲刺(6/10)
    WeChair项目Beta冲刺(5/10)
  • 原文地址:https://www.cnblogs.com/webor2006/p/9999063.html
Copyright © 2020-2023  润新知