概述

几乎稍有经验的Android开发,都会在工作中用到JNI的开发。即使工作中没有涉及到JNI的开发,在我们使用第三方的库时,也经常需要引入.so文件。

最初我在学习JNI开发时,基本是懵的。因为大部分JNI开发的指南,其实是在教我们,如何生成.so文件和如何引入.so文件。

我们参照着博客的步骤,修改build.gradle,添加cmakelists,写JNI接口,写c++。但每一步,我们实际是在做啥,我们并没有清晰的认识。这也导致每次JNI的配置步骤,看一次忘一次。

本文旨在彻底写清楚,当我们在做JNI开发时,我们在做什么。

.so文件

so是shared object的缩写,见名思义就是共享的对象,机器可以直接运行的二进制代码。大到操作系统,小到一个专用软件,都离不开so。参见https://en.wikipedia.org/wiki/Library

so主要存在于Unix和Linux系统中。

A shared library or shared object is a file that is intended to be shared by executable files and further shared object files. Modules used by a program are loaded from individual shared objects into memory at load time or run time, rather than being copied by a linker when it creates a single monolithic executable file for the program.

我们通过C/C++开发的软件,如果以动态链接库的形式输出,那么在Android中它的输出就是一个.so文件。

相比于.a,.so文件是在运行时,才会加载的。所以,当我们将.so文件放入工程时,一定有一段Java代码在运行时,load了这个native库,并通过JNI调用了它的方法。

所以,当我们使用JNI开发时,我们就是在开发一个.so文件。不论我们是开发一个工程,还是开发一个库,只要当我们使用C++开发,都会生成对应的.so文件。

所以JNI开发的核心是,我们生成so的过程,和使用so的过程。

生成.so文件

当我们在新建工程过程中,选中support c++时,系统会为我们写好一些配置。

build.gradle

android {

compileSdkVersion 26

defaultConfig {

minSdkVersion 14

targetSdkVersion 26

versionCode 1

versionName "1.0"

externalNativeBuild {

cmake {

// cpp 编译时的额外选项

cppFlags ""

}

}

// 设置 执行 gradle assembleRelease 时,支持的 SO 库构架

ndk{

abiFilters "armeabi" , "armeabi-v7a" , "arm64-v8a"

}

}

buildTypes {

release {

postprocessing {

removeUnusedCode false

removeUnusedResources false

obfuscate false

optimizeCode false

proguardFile 'proguard-rules.pro'

}

}

}

externalNativeBuild {

cmake {

// cmakelists文件的路径

path "CMakeLists.txt"

}

}

}

这是module的build.gradle中的一段。其中,两个externalNativeBuild与ndk是与JNI相关的配置。

我们写好的.cpp/.h文件需要经过编译才能生成.so,让apk得以调用。在编译生成.so文件的过程中,我们可以添加如下一些配置。

cppFlags

abiFilters

设置 执行 gradle assembleRelease 时,支持的 SO 库构架。如果像上面的代码这样设置,我们打出来的包,就会包含三种架构的.so包。这必然会使APK包体积变大。可以考虑使用productFlavors进行优化。

cmake.path

指定cmakelists文件的路径。

除了这些必须的标签外,externalNativeBuild中还有很多可以配置的东西,因为不是必需,不在此赘述。如ndkBuild中可以设置c++的版本等配置。

CMakeLists.txt

# For more information about using CMake with Android Studio, read the

# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.4.1)

# Creates and names a library, sets it as either STATIC

# or SHARED, and provides the relative paths to its source code.

# You can define multiple libraries, and CMake builds them for you.

# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.

native2-lib

# Sets the library as a shared library.

SHARED

# Provides a relative path to your source file(s).

src/main/cpp/native-lib.cpp )

# Searches for a specified prebuilt library and stores the path as a

# variable. Because CMake includes system libraries in the search path by

# default, you only need to specify the name of the public NDK library

# you want to add. CMake verifies that the library exists before

# completing its build.

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 )

# Specifies libraries CMake should link to your target library. You

# can link multiple libraries, such as libraries you define in this

# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.

native2-lib

# Links the target library to the log library

# included in the NDK.

${log-lib} )

add_library

该指令的主要作用就是将指定的源文件生成链接文件,然后添加到工程中去。指令语法如下:

add_library( [STATIC | SHARED | MODULE]

[EXCLUDE_FROM_ALL]

[source1] [source2] [...])

find_library

该指令的作用为查找库,指令语法如下:

find_library ( name1 [path1 path2 ...])

target_link_libraries

该指令的作用为将目标文件与库文件进行链接。该指令的语法如下:

target_link_libraries( [item1] [item2] [...]

[[debug|optimized|general] ] ...)

完成以上配置后,当你Make Project时,我们就可以在build目录下,看到我们的.so文件了。

不论是给自己项目用,还是提供给其他项目用。当我们执行我们的C++代码时,我们都使用的是这个.so文件了。

使用.so文件。

将下来,我们就需要关注。Android是如何使用.so文件的了。

在很多如何使用so文件的博客中,我们可以看到下面这一句话:

jniLibs.srcDirs = ['libs']

这句话是将我们的so文件的目录指定为libs文件夹。这样,我们只需要将so文件放入libs即可。

这样,当我们构建APK包时,gradle就会帮我们,将这个.so文件,打入我们的APK文件中。

这样,当我们在代码中,执行到

System.loadLibrary("native2-lib");

我们就会加载这个libnative2-lib库了。

有趣的问题

在开发中,我们会遇到一个有趣的情况。

比如,我们有工程ProjectA。有库LibraryA。

Project依赖库LibraryA。

LibraryA使用了NDK,生成了.so库。

由于ProjectA依赖了库LibraryA,所以当我们构建ProjectA时,一定会先构建LibraryA。此时,我们就已经生成了libnative2-lib.so。

如果我们再将libnative2-lib.so放入ProjectA的libs目录时,我们的工程下,其实就有两个libnative2-lib.so了。此时,我们生成APK时,系统如何辨认两个libnative2-lib.so呢。当我们调用

System.loadLibrary("native2-lib");

我们到底加载的是哪个libnative2-lib.so呢?

经过试验,我们发现,APK中,只会有一个libnative2-lib.so。并且,是ProjectA中的libnative2-lib.so。这个地方,我们可以推断出gradle在为我们构建APK时的逻辑。

gradle在构建Android APK 时,一定是指定了好几个工程中存放.so文件的目录。在build时,会遍历这些目录,并将指定目录下的.so文件复制到APK包中。如果名字相同的.so文件,则会相互覆盖。

由于LibraryA是先build的,所以LibraryA中的.so文件会被ProjectA中的.so文件覆盖。

SDK如何输入.so

如果我们开发的就是一个Library。当其他Project集成我们的Library时,需要将我们的.so文件,拷贝到Project的so文件指定路径。

通常来说,我们会在build.gradle中,添加一个小小的脚本。将.so文件在build后,复制到我们的指定目录中。方便使用Project取用。

task(copySoFile) {

String fromDir = './build/intermediates/cmake/debug/obj'

String toDir = [target dir]

copy {

into toDir

from(fromDir) {

include '**'

}

}

println "------------ so files copied --------------"

}

总结

本文记录了当Android需要进行JNI开发,需要明白的一些基础知识。如有问题,欢迎指正。

android 简述jni调用过程,Android JNI 开发相关推荐

  1. android JNI调用(Android Studio 3.0.1)(转)

    最近回头复习了一下android 的jni调用,却发现按以前的方法调用失败,一怒之下就重新摸索,碰了几次壁,发现网上好多教程都不能成功调用,于是记录一下现在AS版本成功好用的调用方法. 这里设定你的n ...

  2. android服务的启动过程,Android Service的启动过程(上)

    原标题:Android Service的启动过程(上) (点击上方公众号,可快速关注) 来源:伯乐在线专栏作者 - xuyinhuan 链接:http://android.jobbole.com/85 ...

  3. android 监听动画过程,Android应用开发之Android动画监听实现方法

    本文将带你了解Android应用开发Android动画监听实现方法,希望本文对大家学Android有所帮助. Android动画监听实现方法. package com.briup.anim; impo ...

  4. android JNI调用过程中的方法签名规则

    有了数据类型之间的对应关系,JNI就可以正确识别并转换Java类型,但是Java支持方法重载,仅靠函数名是无法唯一确定一个方法的.于是JNI提供了一套签名规则,用一个字符串来唯一确定一个方法.其规则如 ...

  5. android jni 调用 java_Android与JNI(二) ---- Java调用C++ 动态调用

    目录: 1. 简介 2. JNI 组件的入口函数 3. 使用 registerNativeMethods 方法 4. 测试 5. JNI 帮助方法 6. 参考资料 1. 简介 Android与JNI( ...

  6. 简述android源代码的编译过程,Android源码编译过程详述

    首先说一下,编译Android所用的系统,目前ubuntu是比较好的平台,也是官方推荐的,但具体版本,说的都比较少,为了避免大家走弯路,我 这里说一下,最好的就是ubuntu 8.10,他所带有的各个 ...

  7. android 和风天气 调用示例,Android中 GsonFormat插件解析Jason 数据+和风天气接口解析案例(示例代码)...

    首先 转载自http://www.cnblogs.com/androidsuperman/p/4579249.html     感谢 @西北野狼  同学. 第一部分: GsonFormat 插件基本使 ...

  8. 基本JNI调用技术(c/c 与java互调)

    注意我就是错在这个些包的名字上,导致调用失败 1,在项目根目录下建立文件夹libs/armeabi文件夹 2,将so库放入libs/armeabi文件夹注意事项: 1,如果采用静态注册的方式请注意C文 ...

  9. Android Bluetooth蓝牙enable过程

    Android Bluetooth蓝牙enable过程 Android Bluetooth框架 enable的追踪从settings app开始,一直到最底层的driver,基本按照上图的框架,把函数 ...

  10. Linux下JNI调用简单实例操作全过程

    开发环境:Linux(Ubuntu 11.04) + JDK 7 实例说明:利用JNI调用本地代码的方法来实现一个计算Int数组总和的功能 使用JNI调用本地代码,整个开发流程主要包括以下几个步骤: ...

最新文章

  1. yolov3(一:模型训练)
  2. 负载均衡环境中和如何设置Expires和Etag
  3. 树莓派(Raspberry Pi)修改时区
  4. java jna_Java中jna的用法
  5. AWS SDK for Java 2.0 使用的基础入门
  6. leetcode(3)---寻找最大字符串
  7. activiti 如何获取下一步节点
  8. 计算机网络基本操作命令的使用,计算机网络-路由器基本命令操作实验指导书--华为...
  9. 蓝色圆形门禁卡怎么模拟_手把手教你把门禁卡复制到手机,跟实体门禁卡说拜拜...
  10. 宇视科技android面试_宇视科技软件笔试面试
  11. 【白皮书分享】2020智能体白皮书-华为.pdf(附下载链接)
  12. eclipse各个版本下载
  13. processing作品代码_创意编程 | Processing的初步学习
  14. ×××的两种组网方式
  15. 在ASP中常见的错误80004005信息和解决办法
  16. ubuntu安装java虚拟机
  17. revit2016与2017区别_【科普】Revit2016版与Revit2018版的区别?
  18. JavaScript入门概念
  19. SaaS公司投资分析时,MRR比ARR指标更有用 | 投资人说
  20. 细胞和基因治疗产品法律监管问题及伦理的研究

热门文章

  1. Kinect开发学习笔记之(一)Kinect介绍和应用
  2. 金融标准化“十四五”规划突出数据安全
  3. 我说CMM2.0之:风险与机会管理
  4. CCNA考试题库中英文翻译版及答案5
  5. 国际IT认证考试题库小程序
  6. bootstrap在线定制工具
  7. Android Layout
  8. python程序实现rep后剪枝算法
  9. fastdfs 原理
  10. MATLAB中的单相与三相dq变换模块