美文网首页
Android NDK开发(一)

Android NDK开发(一)

作者: 达文西_阿泽 | 来源:发表于2022-05-10 17:02 被阅读0次

最近公司转做回收,我这个做Android的就无事可干,等着"毕业",刚好准备一下接下来即将到来的面试等相关信息,顺便复习一下当前比较热门的一些技术,做一下自我总结。以下是在学习NDK开发过程中的一些笔记,以及自身的一些理解。

NDK的一般使用场景

1.重用一些现成的库,例如已经用C/C++编写好的openCV库
2.前面提到的高性能计算,例如很多Bitmap的处理到放在NDK进行处理。
3.一些敏感信息的保护,例如密钥等信息(密钥等信息还是要经过加盐才能放到NDK中,不然还是会有别反编译的风险)

NDK开发环境配置

NDK:Android原生开发套件
CMAKE:外部编译工具
LLDB:原生代码调试工具

下载完之后便可以进行配置,配置的过程可进行百度,具体的位置在settings->Appearance&Behavior->System Settings->Android SDK->SDK Tools中,值得注意的是,在比较高版本的IDEA中,LLDB已经内置在工具中,所以当你在SDK Tools中找不到LLDB工具时,说明你的工具已经自带了,无需自行安装;


NDK开发的简单流程

首先要创建你的c++原生库文件,在你的native module中的src/main目录下创建一个放置C++以及头部文件的目录jni(有的人喜欢改为cpp,这个目录的名称是自定义的),然后New>C/C++ Source File,输入一个名称,例如my-ndk;由于我用的是idea,不是Android studio,所以我直接手动创建my-ndk.cpp文件;
然后创建我们的本地代码,比如我这里:

package com.hyz.sdk;
public class Utils {
    static {
        System.loadLibrary("my-ndk");
    }

    public static native String getString();
}

然后进入到该类的目录中,调用命令行javah即可生成.h头部文件,比如我这里是:
javah com.hyz.sdk.Utils; 这里的命令行还没有添加相关的参数,比如输出目录什么的,为了方便创建构建.h头部文件,避免每次都需要我们手动去敲一遍javah命令,我们可以为IDEA添加Javah命令工具,我使用的是windows,具体位置如下:

javah扩展工具配置
具体的配置

program: javah的路径
arguments: -encoding UTF-8 -jni -classpath $OutputPath$ -d $ContentRoot$\src\main\jni $FileClass$
working directory: $ContentRoot$\src

在使用javah命令工具构建之前,你需要先build一下我们的module项目。而后右键选择extend tools中javah工具便可创建我们的.h文件头,这个 头文件其实就是Java与C/C++之间的桥梁JNI;

》》扩展:构建SO动态库

构建SO动态库跟上面的java与C++代码相互间的调用完全没有关系,只是单纯的想要创建能给给到外部调用的so文件而已。因为我们不可能直接将C++源码丢给别人,否则为何还这么麻烦,直接使用java不是更好吗对吧!!构建动态so库有有两种方式:一种是 ndk-build + Android.mk + Application.mk 的方式,另一种是CMake + CMakeLists.txt 的方式,后者是AS 2.2之后,工具中添加的支持,是目前比较简单且推荐使用的方式。下面是CSDNByteSaid在博客中关于这两者的使用推荐比较:

如果非必须,不推荐使用 ndk-build 来构建,因为这样构建源码后,是无法使用方法跳转、方法提示等功能的!如果要改代码,就等于文本编辑器写代码。相反 CMake 是支持这些的,因此更有助于提高开发效率。如果是新建项目就使用 CMake,如果是使用 ndk-build 的老项目,也可以按照下面的步骤转为CMake方式。

1.既然是推荐,那就先介绍第二种使用CMake+CMakeLists的方式:
首先,第一步、在项目目录下,创建CMakeLists.txt文件,当然如果你在创建项目的时候已经指定了该项目是Native C++项目,它就已经自动帮你添加了该文件;文件的内容如下:

#指定最低版本
cmake_minimum_required(VERSION 3.4.1)

#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})

#添加动态库,如果有多个,可以有多个add_library
add_library( # 设置你的动态库名称
             my-ndk-name
             # 模式
             SHARED
             # 提供动态库的文件相对路径
             src/main/jni/my-ndk.cpp )

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 )

#最终的库
target_link_libraries( # 跟上面的动态库名称相同
                       my-ndk-name
                       # Links the target library to the log library
                       # included in the NDK.
                       ${log-lib} )
#添加相关的依赖
include_directories(src/main/jni/include/)

值得注意的是上面的set(CMAKE_LIBRARY_OUTPUT_DIRECTORY {PROJECT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
这是指定生成的so文件的输出路径,(这里我是指定了在项目根目录中创建的jniLibs目录专门来存放项目生成的so文件),当然你也可以不指定,那默认的输出路径就是在module项目的build\intermediates\cmake\debug(release)\obj目录中。

CMakeLists方式构建图
第二步,修改module下的build.gradle
    externalNativeBuild {
  externalNativeBuild {
            cmake {
                //cppFlags "-std=c++11 -stdlib=libc++ -fPIC -w"
                cppFlags ""
              //特定参数
              // arguments "-DANDROID_TOOLCHAIN=clang", "-DANDROID_STL=c++_shared", "-DANDROID_ARM_MODE=arm", "-DANDROID_ARM_NEON=TRUE", "-DANDROID_PLATFORM=android-21"
                //表示需要生成的so库目录
                abiFilters 'arm64-v8a','armeabi-v7a','x86'
            }
        }
        cmake {
            path file('CMakeLists.txt')
        }
    }

至此CMake的配置方式就此完成,点击module,rebuild一下便可以在输出目录看到对应的可使用的so文件了。

2.当然咯,有些人可能会想要使用ndk-build构建方式,或者说有的老项目使用的就是ndk-build构建方式,自己出于某些原因不想修改,那接下来也给出ndk-build的构建方式。
首先使用ndk-build构建方式,本质上跟CMake是没有什么区别的。
ndk-build本质上是一个脚本,他的位置就在NDK目录的最上层,即在<NDK>/ndk-build路径下。所以我们可以给IDEA配置一个扩展的工具,来快速的构建ndk,而不需要每次都手动构建,类似上面的javah工具,在settings->Tools->External Tools中添加如下工具:

ndk-build扩展工具构建图
在使用ndk-build构建so时,有两个重要的配置文件,Android.mkApplication.mk注意不要写错名称哦!!将这两个文件放在自己的.cpp文件所在的目录中(比如我的是jni).
由于这两个文件中的语法并不是咱们的重点,所以大致了解配置中的各个字段的说明即可。

Android.mk一般写法:

#指定该mk的路径,$(call my-dir)调用NDK内部的函数获取当前mk文件的路径(固定写法)
LOCAL_PATH := $(call my-dir)

#清空了除了LOCAL_PATH之外的所有LOCAL_xxx变量的值,(固定写法)
#这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。
include $(CLEAR_VARS)
#.............对于模块参数的设置,主要包括:模块名字、模块源文件、模块类型、编译好的模块存放位置、以及编译的平台等...........
#LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块,这是一个名字,系统会自动添加适当的前后缀,比如libxxx,下面将生成libmyndk
LOCAL_MODULE := myndk
#LOCAL_SRC_FILES变量必须包含将要打包如模块的C/C++ 源码,不必列出头文件,系统会自动帮我们找出依赖文件,这里请填写自己的cpp文件
LOCAL_SRC_FILES := my-ndk.cpp
#.................................
include $(BUILD_SHARED_LIBRARY)
Android.mk

Application.mk的一般写法:

#过滤,跟cmake方式中的`abiFilters `是一样的作用。
APP_ABI := armeabi-v7a arm64-v8a
#NDK 构建系统提供了由 Android 系统给出的最小 C++ 运行时库 (system/lib/libstdc++.so)的 C++ 头文件
APP_STL := c++_shared
#指定创建的动态库的平台
APP_PLATFORM :=android-21
#该变量是可选的,用来定义 “release” 或者 “debug” ,“release” 模式是默认的
APP_OPTIM := release

至此ndk-build配置方式便配置成功了,右键你的native module目录下的任意一个文件,选择extend tools中的ndk-build工具,便可生成,当然,你也可以通过进入到module目录,打开doc命令行,输入ndk-build命令也是可以的。


ndk-build构建so

如此同时,会生成两个目录,一个libs目录,里面放置的就是你的so文件,一个名为obj的目录,这个 是中间目录可以不用理会,是不是跟之前cmake生成的默认目录一样呢,里面同样存在有你需要的so文件,这里我想应该也是可以跟cmake一样修改输出目录的,具体的这里就不作介绍了。
以下是两种构建方式,配置文件中参数的相互对应表:


》》SO库文件的引用与使用

要是用已经打好的so库文件或者以来第三方库的so文件,首先需要将so库文件放置在libs目录或者自定义的目录中(比如有些人喜欢放在src目录下的jniLibs目录中),然后再module下的build.gradle中引用so库,具体如下:

android {
    //...
    defaultConfig {
       //version,versioncode,applicationID等信息
        ndk {
            //针对自己项目的架构对应添加相应的so目录
            //目前的手机架构基本上都是arm架构,x86的基本上没有,基本上是平板
            abiFilters "armeabi-v7a",//arm架构的32位
                    "armeabi",//十年前的手机CPU架构,基本上已经不存在了
                    "arm64-v8a",//arm架构的64位
                    "x86",//x86架构的 32位
                    "x86_64"//x86架构的64位
        }
    }
      //省略其他配置...
    sourceSets {
        main {
            //这里的libs需要替换成你放置so库的目录,比如jniLibs
            jniLibs.srcDirs = ['libs']
        }
    }
}

dependencies {
   //省略其他配置....
}

注意当你的使用so跟构建so都在同一个module中的时候,这里的abiFilters会覆盖掉上面构建so库文件过程中配置的'abiFilters'。故此不建议将构建so跟使用so都放在同一个项目中,不熟悉可能会感觉有点乱。可以单独将需要构建so库的代码放在一个以来项目中进行构建。


参考链接:
https://blog.csdn.net/weixin_42011443/article/details/117307993
https://blog.csdn.net/weixin_34583170/article/details/94864797
https://blog.csdn.net/hello_1995/article/details/108875866

相关文章

网友评论

      本文标题:Android NDK开发(一)

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