使用AndroidStudio中的CMake开发JNI

一、前言

在平时的Android开发的过程当中,我们可能会接触到需要要与底层交互的场景,例如对数据的加密和解密我们一般会将这部分工作放在jni层来做,这样减少了别人对我们APK反编译的威胁,在jni层我们再对so库进行加壳后更进一步的保证了我们核心东西的安全,现在大部分的开发都是用Android Studio了,本文简单的介绍一下在Android Studio中开发jni的方法。

在Android Studio 2.2及更高的版本中,推出了Cmake来进行Jni的开发,这个工具非常的方便,下面就介绍一下它的使用。

二、CMake的使用

1.环境的搭建

要使用CMake的功能,我们首先需要安装该插件,我们可以通过如下方式进行安装:
1. 在打开的项目中,从菜单栏选择 Tools > Android > SDK Manager。
1. 点击 SDK Tools 标签。
1. 选中 LLDB、CMake 和 NDK 旁的复选框,如图下图所示。

Cmake安装环境

Cmake:一款外部构建工具,可与 Gradle 搭配使用来构建原生库。
LLDB:一种调试程序,Android Studio 使用它来调试原生代码。
NDK:是开发JNI少不了的,不管你是用ndk-build 还是CMake。

2.创建项目

我们来到创建项目页面,勾选对C++的支持,如下图所示:
创建项目01

接下来就和一直点next就行,但是到最后一个页面会出现一些选项需要我们选择,如下:

  1. C++ Standard:使用下拉列表选择您希望使用哪种 C++ 标准。选择 Toolchain Default 会使用默认的 CMake 设置。
  2. Exceptions Support:如果您希望启用对 C++ 异常处理的支持,请选中此复选框。如果启用此复选框,Android Studio 会将 -fexceptions 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。
  3. Runtime Type Information Support:如果您希望支持 RTTI,请选中此复选框。如果启用此复选框,Android Studio 会将 -frtti 标志添加到模块级 build.gradle 文件的 cppFlags 中,Gradle 会将其传递到 CMake。

项目创建完成后,我们会看到如下目录结构:
项目结构

在我们的工程目录下,系统为我们自动生成了一个cpp的文件夹,里面有一个native-lib.cpp的文件,该文件就是一个c++变现的native文件。并且项目的根目录下多了一个CMakeLists.txt文件,该文件就是我们的构建脚本,给CMake编译的时候使用的。

图左边是我们项目的build.gradle文件,里面多了两项设置,这两项设置是Gradle进行构建是对CMake的配置,在cppFlags中有两个参数,是因为我们创建项目的时候勾选了Exception Support和RTTI。

此时我们运行项目,我们就可以查看到页面上显示了从so文件中传递过来的Hello from C++。那么我们的so文件在什么位置了?

我们将项目切换为Project的方式显示,so的路径为:项目目录\app\build\intermediates\transforms\mergeJniLibs\debug\folders\2000\1f\main\lib\arm64-v8a\libnative-lib.so 这只是一个目录,系统默认为我们生成了好几种CPU架构的so文件,题外话,这个特别的重要,如果我们在arm的cpu下加载了一个x86的so库,从名字上我们看不出差别,但是系统会加载失败。

接下来我们就来查看下,我们生成的apk中是否将该so文件打进了我们的apk压缩包中,我们使用AS的Build下的Analyze apk功能选中我们Build目录下的outputs中的debug标签的apk,如下图所示:
apk结构

可以看到我们的so文件已经打包进了apk当中,并且每一种架构都有,这样的好处是适配了多种cpu的架构,但是我们的apk体积也随之增加了。下面介绍如何指定只打包指定的CPU架构的so文件。

3.CMake脚本文件的配置

上面简单的介绍了我们如何创建一个项目,下面我们就来分析一下CMakeLists.txt脚本文件,系统的初始文件如下:
CMakeLists

  • 第一行设置一个CMake的最小版本,使其知道我们构建的特性,默认就行。
  • add_library()函数用于添加我们创建的native代码依赖,例如(.cpp/.c)结尾的文件
    add_library( # Specifies the name of the library.
                 native-lib

                 # Sets the library as a shared library.
                 SHARED

                 # Provides a relative path to your source file(s).
                 src/main/cpp/native-lib.cpp )

    例如这句代码,我们添加了一个native-lib的库,它在src/main/cpp/目录下的名字为native-lib.cpp,当我们编译完成后,打包apk中的lib目录下是,它的名字会变成libnative-lib.so

    我们可以在程序中这样加载代码:
    System.loadLibrary("native-lib");
  • 为了确保CMake在编译的时候定位到我们的头文件,我们可以使用如下函数:
    add_library(...)

    # Specifies a path to native header files.
    include_directories(src/main/cpp/include/)
  • 当我们需要一些 Android NDK 提供的原生 API 和库。可以通过将 NDK 库包含到项目的 CMakeLists.txt 脚本文件中,就能使用这些 API 中的任意一种,甚至一些NDK库已经存在Android的平台上,我们无需再构建打包到APK中。我们就可以通过如下的代码来引用它们,并与我们的原生库进行关联
    find_library( # Defines the name of the path variable that stores the
                  # location of the NDK library.
                  log-lib

                  # Specifies the name of the NDK library that
                  # CMake needs to locate.
                  log )
    # Links your native library against one or more other native libraries.
    target_link_libraries( # Specifies the target library.
                           native-lib

                           # Links the log library to the target library.
                           ${log-lib} )
  • 上面的方法我们是关联一个现成的库,我们还可以关联一个源代码,如下所示:
    add_library( app-glue
                 STATIC
                 ${ANDROID_NDK}/sources/android/native_app_glue/android_native_app_glue.c )

    # You need to link static libraries against your shared native library.
    target_link_libraries( native-lib app-glue ${log-lib} )
  • 在开发的时候我们可能会用到一些第三方的so库,这是我们该如何关联它们了?

    1.我们首先在项目的src路径下使用AS的功能,创建一个jni目录File-》new Folder-》jni Folder
    目录结构如下:
    这里写图片描述

    2.然后我们在项目的build.gradle中添加jni的目录信息,如下所示:

     sourceSets {
        main {
            jniLibs.srcDirs = ['src/main/jni']
        }
    }

    3.接下来在Cmakelists.txt脚本文件中引入如下代码:

        #设置引入库的路径
     set(distribution_DIR ${CMAKE_SOURCE_DIR}/src/main/jni)
    
    add_library( # Sets the name of the library.
                observer
    
                 SHARED
                 IMPORTED#标示我们引入的是外部库,无需编译
                )
     #设置外部库的路径           
    set_target_properties( # Specifies the target library.
                          observer
    
                         PROPERTIES IMPORTED_LOCATION
    
                         ${distribution_DIR}/${ANDROID_ABI}/libobserver.so
                          )

    接下来我们就可以进行编译,使用analyze apk查看我们的apk,就可以看见我们的so文件一起被打包进去了。

  • 上面我们提过,如果我们把每一种架构都打进我们的apk的话,会增加我们apk的大小,有些时候我们是不需要这样的,例如内置应用只需要内置的一款手机就行了,那么我们就需要进行如下配置,来让gradle只打包我们指定的架构的so

    ndk {
            abiFilters 'armeabi'
        }
    这里我们只指定了armeabi一种类型,当然可以指定好几种,看个人需求了。

注意:在上面我们提到了,我们自己创建.c或则.cpp文件进行开发的时候,我们需要将add_library()函数分开写,不应该写在一个里面,意思就是几个库函数就有几个这个函数,但是target_link_libraries()函数可以写在一起

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值