最近公司转做回收,我这个做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,具体位置如下:
具体的配置
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目录中。
第二步,修改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构建so时,有两个重要的配置文件,
Android.mk
和Application.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
网友评论