安卓JNI精细化讲解,让你彻底了解JNI(一):环境搭建与HelloWord
目录
1、基础概念
├──1.1、JNI
├──1.2、NDK
├──1.3、CMake与ndk-build
2、环境搭建
3、Native C++ 项目(HelloWord案例)
├── 3.1、项目创建(java、kotlin)
├── 3.2、CMake的应用(详细讲解)
├── 3.3、ndk-build的应用(详细讲解)
1、基础概念
1.1、JNI
JNI(Java Native Interface)Java本地接口,使得Java与C/C++具有交互能力
1.2、NDK
NDK(Native Development Kit) 本地开发工具包,允许使用原生语言(C和C++)来实现应用程序的部分功能
Android NDK开发的主要作用:
1、特定场景下,提升应用性能;
2、代码保护,增加反编译难度;
3、生成库文件,库可重复使用,也便于平台、项目间移植;
1.3、CMake与ndk-build
当我们基于NDK开发出native功能后,通常需要编译成库文件,给Android项目使用。
目前,有两种主流的编译方式:CMake__与__ndk-build
__CMake__与__ndk-build__是两种不同的编译工具(与Android代码和C/C++代码无关)
CMake
CMake是Androidstudio2.2之后引入的跨平台编译工具(特点:简单易用,2.2之后是默认的NDK编译工具)
如何配置:
1、创建CMakeLists.txt文件,配置CMake必要参数;
2、使用gradle配置CMakeLists.txt以及native相关参数;
如何编译库文件:
1、Android Studio执行Build即可;
ndk-build
ndk-build是NDK中包含的脚本工具(可在NDK目录下找到该工具,为了方便使用,通常配置NDK的环境变量)
如何配置:
1、创建Android.mk文件,配置ndk-build必要参数;
2、可选创建application.mk文件,配置ndk-build参数 (该文件的配置项可使用gradle的配置替代);
3、使用gradle配置Android.mk以及native相关参数;
2、如何编译库文件(两种方式):
1、Android Studio执行Build即可(执行了:Android.mk + gradle配置);
2、也可在Terminal、Mac终端、cmd终端中通过ndk-build命令直接构建库文件(执行了:Android.mk)
2、环境搭建
JNI安装
JNI 是JDK里的内容,电脑上正确安装并配置JDK即可 (JDK1.1之后就正式支持了);
NDK安装
可从官网自行下载、解压到本地,也可基于AndroidStudio下载解压到默认目录;
编译工具安装
cmake 可基于AndroidStudio下载安装;
ndk-build 是NDK里的脚本工具,NDK安装好即可使用ndk-build;
当前演示,使用的Android Studio版本如下(当前最新版):
启动Android Studio --> 打开SDK Manager --> SDK Tools,如下图所示:
我们选择NDK、CMake、LLDB(调试Native时才会使用),选择Apply进行安装,等安装成功后,NDK开发所依赖的环境也就都齐全了。
3、Native C++ 项目(HelloWord案例)
3.1、项目创建(java / kotlin)
新建项目,选择 Native C++,如下图:
新创建的项目,默认已包含完整的native 示例代码、cmake配置 ,如下图:
这样,我们就可以自己定义Java native方法,并在cpp目录中写native实现了,很方便。
但是,当我们写完native的实现代码,希望运行APP,查看JNI的交互效果,此时,就需要使用编译工具了,咱们还是先看一下Android Studio默认的Native编译方式吧:CMake
3.2、CMake的应用
在CMake编译之前,咱们应该先做哪些准备工作?
1、NDK环境是否配置正确?
-- 如果未配置正确是无法进行C/C++开发的,更不用说CMake编译了
2、C/C++功能是否实现?
-- 此次演示主要使用系统默认创建的native-lib.cpp文件,关于具体如何实现:后续文章再详细讲解
3、CMakeLists.txt是否创建并正确配置?
-- 该文件是CMake工具编译的基础,未配置或配置项错误,均会影响编译结果
4、gradle是否正确配置?
-- gradle配置也是CMake工具编译的基础,未配置或配置项错误,均会影响编译结果
除此之外,咱们还应该学习CMake的哪些重要知识?
1、CMake工具编译生成的库文件默认在什么位置?apk中库文件又是在什么位置?
2、CMake工具如何指定编译生成的库文件位置?
3、CMake工具如何指定生成不同CPU平台对应的库文件?
带着这些问题,咱们开始CMake之旅吧:
3.2.1、NDK环境检查
编译前,建议先检查下工程的NDK配置情况(不然容易报一些乱七八糟的错误):
File --> Project Structure --> SDK Location,如下图(我本地的Android Studio默认没有给配置NDK路径,那么,需要自己手动指定一下):
3.2.2、C/C++功能实现
因为本节主讲CMake编译工具,代码就不单独写了,咱们直接使用工程默认生成的native-liv.cpp,简单调整一下native实现方法的代码吧(修改返回文本信息):
因Native C++工程默认已配置好了CMakeLists.txt和gradle,所以咱们可直接运行工程看效果,如下图:
JNI交互效果我们已经看到了,说明CMake编译成功了。那么,这究竟是怎么做到的呢?咱们接着分析一下吧:
3.2.3、CMake生成的库文件与apk中的库文件
安卓工程编译时,会执行CMake编译,在 工程/app/build/.../cmake/ 中会产生对应的so文件,如下图:
继续编译安卓工程,会根据build中的内容,生成我们的*.apk安装包文件。我们找到、反编译apk安装包文件,查找so库文件。原来在apk安装包中,so库都被存放在lib目录中,如下图:
3.2.4、CMake是如何编译生成so库的呢?
在前面介绍CMake定义时,提到了CMake是基于CMakeLists.txt文件和gradle配置实现编译Native类的。那么,咱们先来看一下CMakeLists.txt文件吧:
#cmake最低版本要求
cmake_minimum_required(VERSION 3.4.1)
#添加库
add_library(
# 库名
native-lib
# 类型:
# SHARED 是指动态库,对应的是.so文件
# STATIC 是指静态库,对应的是.a文件
# 其他类型:略
SHARED
# native类路径
native-lib.cpp)
# 查找依赖库
find_library(
# 依赖库别名
log-lib
# 希望加到本地的NDK库名称,log指NDK的日志库
log)
# 链接库,建立关系( 此处就是指把log-lib 链接给 native-lib使用 )
target_link_libraries(
# 目标库名称(native-lib 是咱们要生成的so库)
native-lib
# 要链接的库(log-lib 是上面查找的log库)
${log-lib})
实际上,CMakeList.txt可配置的内容远不止这些,如:so输出目录,生成规则等等,有需要的同学可查下官网。
接着,咱们再看一下app的gradle又是如何配置CMake的呢?
apply plugin: 'com.android.application'
android {
compileSdkVersion 29
buildToolsVersion "29.0.1"
defaultConfig {
applicationId "com.qxc.testnativec"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
//定义cmake默认配置属性
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
//定义cmake对应的CMakeList.txt路径(重要)
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
实际上,gradle可配置的cmake内容也远不止这些,如:abi、cppFlags、arguments等,有需要的同学可查下官网。
3.2.5、如何指定库文件的输出目录?
如果希望将so库生成到特定目录,并让项目直接使用该目录下的so,应该如何配置呢?
比较简单:需要在CMakeList.txt中配置库的输出路径信息,即:
CMAKE_LIBRARY_OUTPUT_DIRECTORY
# cmake最低版本要求
cmake_minimum_required(VERSION 3.4.1)
# 配置库生成路径
# CMAKE_CURRENT_SOURCE_DIR是指 cmake库的源路径,通常是build/.../cmake/
# /../jniLibs/是指与CMakeList.txt所在目录的同级目录:jniLibs (如果没有会新建)
# ANDROID_ABI 生成库文件时,采用gradle配置的ABI策略(即:生成哪些平台对应的库文件)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})
# 添加库
add_library( # 库名
native-lib
# 类型:
# SHARED 是指动态库,对应的是.so文件
# STATIC 是指静态库,对应的是.a文件
# 其他类型:略
SHARED
# native类路径
native-lib.cpp)
# 查找依赖库
find_library(
# 依赖库别名
log-lib
# 希望加到本地的NDK库名称,log指NDK的日志库
log)
# 链接库,建立关系( 此处就是指把log-lib 链接给native-lib使用 )
target_link_libraries(
# 目标库名称(native-lib就是咱们要生成的so库)
native-lib
# 要链接的库(上面查找的log库)
${log-lib})
还需要在gradle中配置 jniLibs.srcDirs 属性(即:指定了lib库目录):
sourceSets {
main {
jniLibs.srcDirs = ['jniLibs']//指定lib库目录
}
}
接着,重新build就会在cpp相同目录级别位置生成jniLibs目录,so库也在其中了:
注意事项:
1、配置了CMAKE_CURRENT_SOURCE_DIR,并非表示编译时直接将so生成在该目录中,实际编译时,so文件仍然是
先生成在build/.../cmake/中,然后再拷贝到目标目录中的
2、如果只配置了CMAKE_CURRENT_SOURCE_DIR,并未在gradle中配置 jniLibs.srcDirs,也会有问题,如下:
More than one file was found with OS independent path 'lib/arm64-v8a/libnative-lib.so'
此问题是指:在编译生成apk时,发现了多个so目录,android studio不知道使用哪一个了,所以需要咱们
告诉android studio当前工程使用的是jniLibs目录,而非build/.../cmake/目录
3.2.5、如何生成指定CPU平台对应的库文件呢?
我们可以在cmake中设置abiFilters,也可在ndk中设置abiFilters,效果是一样的:
defaultConfig {
applicationId "com.qxc.testnativec"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
abiFilters "arm64-v8a"
}
}
}
defaultConfig {
applicationId "com.qxc.testnativec"
minSdkVersion 21
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild {
cmake {
cppFlags ""
}
}
ndk {
abiFilters "arm64-v8a"
}
}
按照新的配置,我们重新运行工程,如下图:
再反编译看下工程,果然只有arm64-v8a的so库了,不过库文件在apk中仍然是放在lib目录,而非jniLibs(其实也很好理解,jniLibs只是我们本地的目录,便于我们管理库文件,真正生成apk时,仍然会按照lib目录放置库文件),如下图:
至此,CMake的主要技术点都讲完了,接下来咱们看下NDK-Build吧~
3.3、ndk-build的应用
在ndk-build编译之前,咱们又应该先做哪些准备工作?
1、ndk-build环境变量是否正确配置?
-- 如果未配置,是无法在cmd、Mac终端、Terminal中使用ndk-build命令的(会报错:找不到命令)
2、NDK环境是否配置正确?
-- 如果未配置正确是无法进行C/C++开发的,更不用说ndk-build编译了
3、C/C++功能是否实现?
-- 此次演示主要使用系统默认创建的native-lib.cpp文件,关于具体如何实现:后续文章再详细讲解
-- 注意:为了与CMake区分,咱们新建一个“jni”目录存放C/C++文件、配置文件吧
4、Android.mk是否创建并正确配置?
-- 该文件是ndk-build工具编译的基础,未配置或配置项错误,均会影响编译结果
5、gradle是否正确配置?(可选项,如果通过cmd、Mac终端、Terminal执行ndk-build,可忽略)
-- gradle配置也是ndk-build工具编译的基础,未配置或配置项错误,均会影响编译结果
6、Application.mk是否创建并正确配置?(可选项,一般配ABI、版本,这些项均可在gradle中配置)
-- 该文件也是ndk-build工具编译的基础,未配置或配置项错误,均会影响编译结果
除此之外,咱们还应该学习ndk-build的哪些重要知识?
1、ndk-build工具如何指定编译生成的库文件位置?
2、ndk-build工具如何指定生成不同CPU平台对应的库文件?
带着这些问题,咱们继续ndk-build之旅吧:
3.3.1、环境变量配置
介绍NDK-Build定义时,提到了其实它是NDK的脚本工具。那么,咱们还是先进NDK目录找一下吧,ndk-build工具的位置如下图:
如果我们希望任意情况下都能便捷的使用这种脚本工具,通常做法是配置其环境变量,否则我们在cmd、Mac终端、Terminal中执行 ndk-build 命令时,会报错:“未找到命令”
配置NDK的环境变量,也很简单,以Mac电脑举例(如果是Windows电脑,网上也有很多关于配置环境变量的文章,如果有需要可自行查下):
1、打开命令终端,输入命令: open -e .bash_profile,打开bash_profile配置文件
2、写入如下内容(NDK_HOME指向 ndk-build 所在路径):
export NDK_HOME=/Users/xc/SDK/android-sdk-macosx/ndk/20.1.5948944
export PATH=$PATH:$NDK_HOME
3、生效.bash_profile配置
source .bash_profile
当我们在cmd、Mac终端、Terminal中执行 ndk-build 命令时,如果出现下图所示内容,则代表配置成功了:
3.3.2、C/C++功能实现
咱们使用比较常用的一种ndk-build方式吧:ndk-build + Android.mk + gradle配置
项目中新建jni目录,拷贝一份CMake的代码实现吧:
1、新建jni目录
2、拷贝cpp/native-lib.cpp 至 jni目录下
3、重命名为haha.cpp (与CMake区分)
4、调整一下native实现方法的文本(与CMake运行效果区分)
5、新建Android.mk文件
接着,编写Android.mk文件内容:
#表示Android.mk所在目录
LOCAL_PATH := $(call my-dir)
#CLEAR_VARS变量指向特殊 GNU Makefile,用于清除部分LOCAL_变量
include $(CLEAR_VARS)
#模块名称
LOCAL_MODULE := haha
#构建系统用于生成模块的源文件列表
LOCAL_SRC_FILES := haha.cpp
#BUILD_SHARED_LIBRARY 表示.so动态库
#BUILD_STATIC_LIBRARY 表示.a静态库
include $(BUILD_SHARED_LIBRARY)
配置gradle:
apply plugin: 'com.android.application'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.aaa.testnative"
minSdkVersion 16
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
//定义ndkBuild默认配置属性
externalNativeBuild {
ndkBuild {
cppFlags ""
}
}
}
//定义ndkBuild对应的Android.mk路径(重要)
externalNativeBuild {
ndkBuild{
path "src/main/jni/Android.mk"
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
现在,native代码、ndk-build配置都完成了,咱们运行看一下效果吧,如下图:
3.3.4、如何指定库文件的输出目录?
通常,可在Android.mk文件中配置NDK_APP_DST_DIR
指定源目录与输出目录(与CMake类似)
#表示Android.mk所在目录
LOCAL_PATH := $(call my-dir)
#设置库文件的输入目录
#输出目录 ../jniLibs/
#源目录 $(TARGET_ARCH_ABI)
NDK_APP_DST_DIR=../jniLibs/$(TARGET_ARCH_ABI)
#CLEAR_VARS变量指向特殊 GNU Makefile,用于清除部分LOCAL_变量
include $(CLEAR_VARS)
#模块名称
LOCAL_MODULE := haha
#构建系统用于生成模块的源文件列表
LOCAL_SRC_FILES := haha.cpp
#BUILD_SHARED_LIBRARY 表示.so动态库
#BUILD_STATIC_LIBRARY 表示.a静态库
include $(BUILD_SHARED_LIBRARY)
3.3.5、如何生成指定CPU平台对应的库文件呢?
可在gradle中配置abiFilters(与Cmake类似)
externalNativeBuild {
ndkBuild {
cppFlags ""
abiFilters "arm64-v8a"
}
}
externalNativeBuild {
ndkBuild {
cppFlags ""
}
}
ndk {
abiFilters "arm64-v8a"
}
3.3.6、如何在Terminal中直接通过ndk-build命令构建库文件呢?
除了执行AndroidStudio的build命令,基于gradle配置 + Android.mk编译生成库文件,我们还可以在cmd、Mac 终端、Terminal中直接通过ndk-build命令构建库文件,此处以Terminal为例进行演示吧:
先进入包含Android.mk文件的jni目录(Android Studio中可直接选中jni目录并拖拽到Terminal中,会自动跳转到该目录),再执行ndk-build命令,如下图:
同样,编译也成功了,如下图:
因是直接在Terminal中执行了ndk-build命令,所以只会根据Android.mk进行编译(不包含gradle配置内容,也就不会执行abiFilters过滤),生成了所有默认CPU平台的so库文件。
ndk-build命令其实也可以配上一些参数使用,此处就不再详解了。日常开发时,还是建议选择CMake作为Native编译工具,因为是安卓主推的,而且更简单一些。
安卓JNI精细化讲解,让你彻底了解JNI(一):环境搭建与HelloWord的更多相关文章
- 安卓JNI精细化讲解,让你彻底了解JNI(二):用法解析
目录 用法解析 ├── 1.JNI函数 │ ├── 1.1.extern "C" │ ├── 1.2.JNIEXPORT.JNICALL │ ├── 1.3.函数名 │ ├── 1 ...
- 【转】android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)
原文网址:http://www.cnblogs.com/zdz8207/archive/2012/11/27/android-ndk-install.html android 最新 NDK r8 在w ...
- Java ee 与安卓环境搭建个人心得
最近加了个IT俱乐部,第一次作业就是搞定eclipse,完成Java ee 与安卓环境搭建.为此我上网看了好多教程,之前我安装了Java,可以说省了不少事,而且还了解一点安装方法.流程网上都有,但是不 ...
- android Jni NDK开发环境搭建及其简单实例的编写
android Jni NDK开发环境搭建及其简单实例的编写 由于工作需要,需要采用开发想要的JNI,由于之前没有接触过安卓的开发,所以更加网上的帖子,学习了下.遇到了些问题,然后总结下学习过程中 ...
- android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创)
android 最新 NDK r8 在window下开发环境搭建 安装配置与使用 详细图文讲解,完整实际配置过程记录(原创) 一直想搞NDK开发却一直给其他事情耽搁了,参考了些网上的资料今天终于把 ...
- HyperFT项目安卓端的环境搭建及编译的图解教程
一.Android studio 3.5安装详解 1.安装IDE 安装前的准备:已安装过的需要卸载,并且删除C:\user\yourname\ 下.android ,gradle, .AndroidS ...
- Android Studio Jni 环境搭建
第一步:NDK环境搭建,点击下图红色框区域查看NDK下载和环境配置 安照正常情况是很慢的或者无法下载成功的,这个时候可以去下载NDK压缩包进行解压.下面给出两个下载地址 (1)官网:http://we ...
- monkeyrunner之安卓开发环境搭建(一)
在学习monkeyrunner之前,让我们先搭建好eclipse安卓开发环境. 对于程序开发人员而言,eclipse并不陌生,它提供了一个非常广阔的平台来开发程序.同样也可以用它来开发android程 ...
- ubuntu12.04下安卓编译环境搭建总结
前言: 因为工作需要,经常要编译安卓下的动态库,公司有已经搭建好环境的服务器,但是第一自己想自己搭建一下了解一个整个过程,另外,公司的服务器也经常出现问 题,导致编译不了,所以就想自己搭建环 ...
随机推荐
- 第三篇-分析日志和sensor-data中的数据结构
分析日志和sensor-data数据结构 该文章提供web端思路,ios和android端思路不提供,api也已经下线,本文也不提供任何可执行代码.有更多疑问欢迎查看github代码 协议 授权协议: ...
- ElasticSearch Bulk API
做一个简单的记录,以便自己后续查找 一.环境要求 ElasticSearch 7.3.0 Kibana 7.3.0 二.详情 ElasticSearch 的 Bulk API 可以批量进行索引或者删除 ...
- MS09-020 iis6.0提权
漏洞编号:MS09-020 披露日期: 2009/6/9 受影响的操作系统:Windows 2003 x64 sp1 sp2;XP; 测试系统:windows 2003 x64 上传 执行 iis ...
- 00jmeter安装相关
1.官网下载安装包:http://jmeter.apache.org/ 下载最新版本: 2.将下载后的zip文件解压 3. jdk与jmeter的环境变量配置(以下变量如果没有则新建,如果已存在则直接 ...
- 3. Rsync-远程同步(下)-企业案例
已知3台服务器主机名分别为web01.backup .nfs主机信息见下表: 角色 外网IP(NAT) 内网IP(LAN) 主机名 WEB eth0:10.0.0.7 eth1:172.16.1.7 ...
- mongodb学习笔记系列一
一.简介和安装 ./bin/mongod --dbpath /path/to/database --logpath /path/to/log --fork --port 27017 mongodb非常 ...
- 【MongoDB详细使用教程】二、MongoDB基本操作
目录 数据类型 数据库操作 集合操作 数据操作 增 查 改 修改整行 修改指定字段的值 删 数据类型 MongoDB常见类型 说明 Object ID 文档ID String 字符串,最常用,必须是有 ...
- NOIP2018提高组初赛游记
AH省的,好像水军多,走的都比较早(莫非是真·大佬!!) 本人考了71,较去年退步了.(去年还考80多的来着) 题目坑.. 第一.二大题选择 第三题年份,看了试卷标题,第二十二届,算出来后没有这个选项 ...
- C#+Selenium抓取百度搜索结果前100网址
需求 爬取百度搜索某个关键字对应的前一百个网址. 实现方式 VS2017 + Chrome .NET Framework + C# + Selenium(浏览器自动化测试框架) 环境准备 创建控制台应 ...
- 百万年薪python之路 -- while循环
day02 1.while循环 -- while关键字 while 空格 条件 冒号 缩进 循环体 while 5>4: print("Hello World!") 数字中非 ...