NDK-JNI实战教程(三) 从比Hello World稍复杂点儿的NDK例子说说模板

来源:互联网 发布:mac如何查看qq群相册 编辑:程序博客网 时间:2024/05/16 13:44

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN。因为CSDN也支持MarkDown语法了,牛逼啊!

【工匠若水 http://blog.csdn.net/yanbober】 阅读前一篇《NDK-JNI实战教程(二) JNI官方中文资料》 http://blog.csdn.net/yanbober/article/details/45310365

第一部分

概述

学习JNI NDK你需要有java与C或者C++基础。因为NDK几乎就是java与C或者C++的混合编程互调,JNI在其中只是扮演了一个不同语种间对接握手调运的规则而已。就像C语言嵌入调运执行汇编程序一样,需要一种规则来约束沟通。这个例子是我在闲时继续使用Android Studio撸的,不难,适合入门。不要一下子被这么几个文件吓着了。重点是为了通过这个例子引出来几个Android NDK开发的重要基础模板知识点。所以内在代码逻辑看上去可能十分僵硬不合理,代码风格可能也不是十分规范,还请多多指点交流,然后撸的更多。

需要知识点:C语言基础,C语言动态参数宏,Java基础,JNI基本概念

代码及工程文件介绍

点我进入完整工程代码示例下载页面

这个例子是一个简单的场景模拟实现;我们通过在app java层传入一个name到c库中,c库通过app传入的name经过保密的自定义加密算法(本代码没实现,只是模拟)处理生成一个客户化定制的key反馈给app层使用。这样至于通过name得到key的具体加密机制被编译成了so文件,很难被破解。而如果使用java则很容易被破解。

这是这篇文章要介绍的代码工程的几个主要文件夹文件分布情况:

JNI

浅析:正常NDK工程目录结构,其中jni目录下只是多包涵了两个文件夹而已。在这里在jni根目录下的两个文件就是jni核心文件,起到C与Java的互联互通作用;utils目录是我自己加入的一个常用工具目录,里面放置一些通用代码,譬如这里的android_log_print.h用来打印log;local_logic_c目录是我放置的用C语言实现的加密逻辑代码,其中包含实现和头文件。你的jni目录结构也可以随意组织,符合自己习惯效率就行。在这里需要注意的一点是Android JNI下面c代码使用printf打印是不显示的,所以才需要像我加入的宏,使用android提供的log打印函数,不过在编译时请记得加入log依赖的官方lib。

io.github.yanbober.ndkapplication包中MainActivity主Activity代码:

<code class="language-android hljs java has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> io.github.yanbober.ndkapplication;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.os.Bundle;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.support.v7.app.ActionBarActivity;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">import</span> android.widget.TextView;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MainActivity</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">ActionBarActivity</span> {</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> TextView mTextView;    <span class="hljs-annotation" style="color: rgb(155, 133, 157); box-sizing: border-box;">@Override</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">onCreate</span>(Bundle savedInstanceState) {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (TextView) <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>.findViewById(R.id.test);        NdkJniUtils jni = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> NdkJniUtils();        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//传入name="vip"到jni代码模拟拿到加密后的key</span>        mTextView.setText(jni.generateKey(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"vip"</span>));    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li></ul>

浅析:这就是App的传统界面了,一个UI传入name=”vip”,调运native方法取到转换好的key显示在TextView里,没啥技术难度。

io.github.yanbober.ndkapplication包中NdkJniUtils类代码:

<code class="language-java hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> io.github.yanbober.ndkapplication;<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">NdkJniUtils</span> {</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">native</span> String <span class="hljs-title" style="box-sizing: border-box;">generateKey</span>(String name);    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> {        System.loadLibrary(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"YanboberJniLibName"</span>);    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

浅析:这个类就是定义本地native方法,编译以后通过javah生成这个文件的h头文件,如下文。其中static块作用就不说了吧。System.loadLibrary(“YanboberJniLibName”);就是加载你编译生成的库文件,注意库生成在lib目下默认会添加lib前缀,形如:libXxx.so,我们在load函数里传入的名字只需要Xxx就行。

jni根目录下通过系列教程一中javah生成的头文件io_github_yanbober_ndkapplication_NdkJniUtils.h内容:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* DO NOT EDIT THIS FILE - it is machine generated */</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <jni.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifndef _Included_io_github_yanbober_ndkapplication_NdkJniUtils</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define _Included_io_github_yanbober_ndkapplication_NdkJniUtils</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifdef __cplusplus</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extern</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"C"</span> {<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span>JNIEXPORT jstring JNICALL Java_io_github_yanbober_ndkapplication_NdkJniUtils_generateKey(JNIEnv *, jobject, jstring);<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifdef __cplusplus</span>}<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

浅析:通过javah生成的头文件,不明白的参考系列教程一中。

jni根目录下通过系列教程一中类似test生成的jni接口c文件jni_interface.c内容:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <jni.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <string.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "io_github_yanbober_ndkapplication_NdkJniUtils.h"</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "./utils/android_log_print.h"</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "./local_logic_c/easy_encrypt.h"</span>JNIEXPORT jstring JNICALL Java_io_github_yanbober_ndkapplication_NdkJniUtils_generateKey  (JNIEnv *env, jobject obj, jstring name){     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//声明局部量</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> key[KEY_SIZE] = {<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>};     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">memset</span>(key, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(key));     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> temp[KEY_NAME_SIZE] = {<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>};     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将java传入的name转换为本地utf的char*</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* pName = (*env)->GetStringUTFChars(env, name, NULL);     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NULL != pName) {        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strcpy</span>(temp, pName);        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strcpy</span>(key, generateKeyRAS(temp));        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//java的name对象不需要再使用,通知虚拟机回收name</span>        (*env)->ReleaseStringUTFChars(env, name, pName);     }     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (*env)->NewStringUTF(env, key);  } </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li></ul>

浅析:jni”接口封装实现”文件,我就叫这名吧,可能好理解些,别把jni想的太高大上。这里面就是实现h文件声明的函数。一些基本参数可以查阅系列教程二文档,复制关键字在教程二里搜索查阅即可。主要流程就是通过GetStringUTFChars拿到java传入的String的name转换后的char* utf-8指针;把name通过generateKeyRAS传入C语言实现的加密逻辑代码中处理,同时通过ReleaseStringUTFChars告诉虚拟机不需要持有name的引用,以便Java释放String的name;完事将C语言处理生成的key通过NewStringUTF转换返回给java层使用。

jni目录下utils子目录下的log打印工具宏android_log_print.h文件内容:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* * 作者:工匠若水 * 说明:Android JNI Log打印宏定义文件 */</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifndef _ANDROID_LOG_PRINT_H_</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define _ANDROID_LOG_PRINT_H_</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <android/log.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define IS_DEBUG</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifdef IS_DEBUG</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOG_TAG ("CUSTOMER_NDK_JNI")</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGV(...) ((void)__android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__))</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG  , LOG_TAG, __VA_ARGS__))</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO   , LOG_TAG, __VA_ARGS__))</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN   , LOG_TAG, __VA_ARGS__))</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR  , LOG_TAG, __VA_ARGS__))</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#else</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGV(LOG_TAG, ...) NULL</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGD(LOG_TAG, ...) NULL</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGI(LOG_TAG, ...) NULL</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGW(LOG_TAG, ...) NULL</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define LOGE(LOG_TAG, ...) NULL</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li></ul>

浅析:这个文件是我自己写JNI时每次直接使用的文件,就是一个工具文件一样。目的是因为Android的JNI使用printf函数打印的东西是没法显示,这里这么转化其实对应的就是java层打印Log的函数Log.d(), Log.i(), Log.w(),Log.e(), Log.f()。原因是因为Android的java层和C++ framework层都提供了Log函数,但是JNI环境下打印稍有不同,使用的是__android_log_print并且用NDK环境编译和android源码framework环境编译选择链接Android.mk库也不同。所以你会发现Google NDK官方sample代码中也是类似处理的,这里只是简单封装的更实用而已。需要一点C语言知识理解。如果你喜欢再往深里折腾,那我再提一点吧,那就是自己去android系统源码的system/core/include/cutils/log.h去看看吧,如果是在完整源码编译环境下,只要include

jni目录下local_logic_c子目录中本地C语言实现的逻辑目录下的接口头文件easy_encrypt.h内容:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifndef _EASY_ENCRYPT_H_</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define _EASY_ENCRYPT_H_</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* * 作者:晏博(工匠若水) * * 功能:通过name获取加密后的key * 类型:测试代码 */</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define KEY_NAME_SIZE  (6)</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define KEY_SIZE  (129)</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* generateKeyRAS(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* name);<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif /* _EASY_ENCRYPT_H_ */</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li></ul>

浅析:这就是标准的C语言模块了,这是逻辑的h文件,不解释。

jni目录下local_logic_c子目录中本地C语言实现的逻辑目录下的接口逻辑实现文件easy_encrypt.c内容:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <string.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "easy_encrypt.h"</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "./../utils/android_log_print.h"</span><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* * 功能:通过传入name生成加密后唯一的key值 * * name 传入小于KEY_NAME_SIZE的字符串 * return 通过name生成的验证key值 */</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* generateKeyRAS(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* name){    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//判断形参是否有效</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NULL == name || <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strlen</span>(name) > KEY_NAME_SIZE) {        LOGD(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"function generateKey must have a ok name!\n"</span>);        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> NULL;    }    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//声明局部变量</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> index = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> loop = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> temp[KEY_SIZE] = {<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"\0"</span>};    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//清空数组内存</span>    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">memset</span>(temp, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(temp));    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将传进来的name拷贝到零时空间</span>    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strcpy</span>(temp, name);    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//进行通过name转化生成key的逻辑,这里是模拟测试,实际算法比这复杂</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">for</span> (index=<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>; index<KEY_SIZE-<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>; index++)    {        temp[index] = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">93</span>;        LOGD(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"---------------temp[%d]=%c"</span>, index, temp[index]);    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> temp;}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li></ul>

浅析:这就是标准的C语言模块了,这是逻辑的c文件,模拟实现了加密算法而已。

build.gradle文件中android.defaultConfig中新加如下代码(其他使用AS编译设置参见本系列教程一):

<code class="language-groovy hljs cs has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;">ndk{    moduleName <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"YanboberJniLibName"</span>    ldLibs <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"log"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"z"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"m"</span>  <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//添加依赖库文件,因为有log打印等</span>    abiFilters <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"armeabi"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"armeabi-v7a"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"x86"</span>}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

浅析:不解释。

编译代码运行在LogCat中可以看见主要的几条Log如下:

JNI

浅析:这里你会看到在运行app时:

  • 尝试加载so文件 Trying to load lib /data/app-lib/io.github.yanbober.ndkapplication-2/libYanboberJniLibName.so 0xa6a4e120
  • 加载了so文件 Added shared lib /data/app-lib/io.github.yanbober.ndkapplication-2/libYanboberJniLibName.so 0xa6a4e120
  • 先不解释这句话 No JNI_OnLoad found in /data/app-lib/io.github.yanbober.ndkapplication-2/libYanboberJniLibName.so 0xa6a4e120, skipping init

上面说“先不解释这句话”的No JNI_OnLoad found……skipping init其实透露出了一个新的知识点,下文会介绍的。

运行程序结果如下:

JNI

浅析:传入name加密后得到的key显示。

点我进入完整工程代码示例下载页面

总结

以上第一部分就是JNI开发常见的基本结构模板,实际开发代码量和文件和目录结构都会比这复杂,这只是一个雏形用来领悟重点。

第二部分

概述

如果你已经大致理解掌握了第一部分内容,那基本OK了。接下来要扯蛋的就是第一部分遗留的历史问题和其他提升技能。

首先,不知道还记不记得第一部分编译代码运行在LogCat中可以看见主要的几条Log。“No JNI_OnLoad found……skipping init”这句话是不是还是依旧耿耿于怀呢?那么接下来咱们放大招来kill它。

从Load这个蛋疼的词说起

Android OS加载JNI Lib的方法有两种:

  • 通过JNI_OnLoad。
  • 如果JNI Lib实现中没有定义JNI_OnLoad,则dvm调用dvmResolveNativeMethod进行动态解析。

PS:咱们上面第一部分就是dvm调用dvmResolveNativeMethod进行动态解析,所以log打印No JNI_OnLoad found。

从网上查到的深入解析(此解析模块代码引用自网络)

JNI_OnLoad机制分析

System.loadLibrary调用流程如下所示:

System.loadLibrary->Runtime.loadLibrary->(Java)nativeLoad->(C: java_lang_Runtime.cpp)Dalvik_java_lang_Runtime_nativeLoad->dvmLoadNativeCode->(dalvik/vm/Native.cpp)

接着如下:

  • dlopen(pathName, RTLD_LAZY) (把.so mmap到进程空间,并把func等相关信息填充到soinfo中)
  • dlsym(handle, “JNI_OnLoad”)
  • JNI_OnLoad->RegisterNatives->dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, const char* signature, void* fnPtr)->dvmUseJNIBridge(method, fnPtr)->(method->nativeFunc = func)

JNI函数在进程空间中的起始地址被保存在ClassObject->directMethods中。

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> ClassObject : Object {      <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* static, private, and <init> methods */</span>      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>             directMethodCount;      Method*         directMethods;      <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* virtual methods defined in this class; invoked through vtable */</span>      <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>             virtualMethodCount;      Method*         virtualMethods;  }</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li></ul>

此ClassObject通过gDvm.jniGlobalRefTable或gDvm.jniWeakGlobalRefLock获取。

dvmResolveNativeMethod延迟解析机制

如果JNI Lib中没有JNI_OnLoad,即在执行System.loadLibrary时,无法把此JNI Lib实现的函数在进程中的地址增加到ClassObject->directMethods。则直到需要调用的时候才会解析这些javah风格的函数 。这样的函数dvmResolveNativeMethod(dalvik/vm/Native.cpp)来进行解析,其执行流程如下所示:

void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self)->(Resolve a native method and invoke it.)

接着如下:

  • void* func = lookupSharedLibMethod(method)(根据signature在所有已经打开的.so中寻找此函数实现)dvmHashForeach(gDvm.nativeLibs, findMethodInLib,(void*) method)->findMethodInLib(void* vlib, void* vmethod)->dlsym(pLib->handle, mangleCM)
  • dvmUseJNIBridge((Method*) method, func)
  • (*method->nativeFunc)(args, pResult, method, self);(调用执行)

说完蛋疼Load基础后该准么办?

答案其实就是推荐Android OS加载JNI Lib的方法的通过JNI_OnLoad。因为通过它你可以干许多自定义的事,譬如实现自己的本地注册等。因为在上面的解析中已经看到了JNI_OnLoad->RegisterNatives->…这两个关键方法。具体细节咱们现在再说说。

先来看JNI_OnLoad函数

JNI_OnLoad()函数主要的用途有两点:

  • 通知VM此C组件使用的JNI版本。如果你的.so文件没有提供JNI_OnLoad()函数,VM会默认该.so使用最老的JNI 1.1版本。而新版的JNI做了许多扩充,如果需要使用JNI的新版功能,例如JNI 1.4的java.nio.ByteBuffer, 就必须藉由JNI_OnLoad()函数来告知VM。 
  • 因为VM执行到System.loadLibrary()函数时,会立即先调运JNI_OnLoad(),所以C组件的开发者可以由JNI_OnLoad()来进行C组件内的初期值之设定(Initialization)。

既然有JNI_OnLoad(),那就有相呼应的函数,那就是JNI_OnUnload(),当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。

再来看RegisterNatives函数

在上面第一部分时我们看见通过javah命令生成的io_github_yanbober_ndkapplication_NdkJniUtils.h里函数的名字好长,看着就蛋疼。你肯定也想过怎么这么长,而且当有时候项目需求原因导致类名变了的时候,函数名必须一个一个的改,更加蛋疼。我第一次接触时那时候自己经验不足,就遇上了这个蛋疼问题。泪奔啊!

既然这样那就有解决办法的,那就是RegisterNatives大招。接下来来看下这个大招:

App的Java程序寻找c本地方法的过程一般是依赖VM去寻找*.so里的本地函数,如果需要连续调运很多次,每次都要寻找一遍,会多花许多时间。因此为了解决这个问题我们可以自行将本地函数向VM进行登记,然后让VM自行调registerNativeMethods()函数。

VM自行调registerNativeMethods()函数的作用主要有两点:  

  • 更加有效率去找到C语言的函数  
  • 可以在执行期间进行抽换,因为自定义的JNINativeMethod类型的methods[]数组是一个名称-函数指针对照表,在程序执行时,可以多次调运registerNativeMethods()函数来更换本地函数指针,从而达到弹性抽换本地函数的效果。

上面提到的JNINativeMethod结构是c/c++方法和Java方法之间映射关系的关键结构,该结构定义在jni.h中,具体定义如下:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">typedef</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> {       <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* name;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//java方法名称   </span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* signature; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//java方法签名  </span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>*       fnPtr;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//c/c++的函数指针  </span>} JNINativeMethod; </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li></ul>

所谓自定义的JNINativeMethod类型的methods[]数组自然也就类似长下面这样了:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> JNINativeMethod methods[] = {          {<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"generateKey"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"(Ljava/lang/String;)Ljava/lang/String;"</span>, (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>*)generateKey},  }; </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>

以上也就是所谓的动态注册JNI了。

好了,该补脑的也差不多了,很空洞很枯燥,空虚寂寞冷啊;接下来进入实战吧,通过对第一部分代码的改变来轻松理解这部分扯淡的内容。

代码实例分析

点我进入完整工程代码示例下载页面

我们对第一部分的jni根目录下的c代码修改如下:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <jni.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <string.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <assert.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "io_github_yanbober_ndkapplication_NdkJniUtils.h"</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "./utils/android_log_print.h"</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include "./local_logic_c/easy_encrypt.h"</span>JNIEXPORT jstring JNICALL native_generate_key(JNIEnv *env, jobject obj, jstring name){     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//声明局部量</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> key[KEY_SIZE] = {<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>};     <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">memset</span>(key, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(key));     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span> temp[KEY_NAME_SIZE] = {<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>};     <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//将java传入的name转换为本地utf的char*</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* pName = (*env)->GetStringUTFChars(env, name, NULL);     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (NULL != pName)     {        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strcpy</span>(temp, pName);        <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">strcpy</span>(key, generateKeyRAS(temp));        <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//java的name对象不需要再使用,通知虚拟机回收name</span>        (*env)->ReleaseStringUTFChars(env, name, pName);     }     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> (*env)->NewStringUTF(env, key);  }<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//参数映射表</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> JNINativeMethod methods[] = {    {<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"nativeGenerateKey"</span>, <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"(Ljava/lang/String;)Ljava/lang/String;"</span>, (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>*)native_generate_key},    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//这里可以有很多其他映射函数</span>};<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//自定义函数,为某一个类注册本地方法,调运JNI注册方法</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> registerNativeMethods(JNIEnv* env , <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* className , JNINativeMethod* gMethods, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> numMethods){    jclass clazz;    clazz = (*env)->FindClass(env, className);    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (clazz == NULL)    {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> JNI_FALSE;    }    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//JNI函数,参见系列教程2</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>)    {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> JNI_FALSE;    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> JNI_TRUE;}<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//自定义函数</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> registerNatives(JNIEnv* env){    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* kClassName = <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"io/github/yanbober/ndkapplication/NdkJniUtils"</span>;<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//指定要注册的类</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> registerNativeMethods(env, kClassName, methods,  <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(methods) / <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">sizeof</span>(methods[<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>]));}JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>* reserved){    LOGD(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"customer---------------------------JNI_OnLoad-----into.\n"</span>);    JNIEnv* env = NULL;    jint result = -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> ((*vm)->GetEnv(vm, (<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>**) &env, JNI_VERSION_1_4) != JNI_OK)    {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;    }    assert(env != NULL);    <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//动态注册,自定义函数</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (!registerNatives(env))    {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> -<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>;    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> JNI_VERSION_1_4;}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li><li style="box-sizing: border-box; padding: 0px 5px;">43</li><li style="box-sizing: border-box; padding: 0px 5px;">44</li><li style="box-sizing: border-box; padding: 0px 5px;">45</li><li style="box-sizing: border-box; padding: 0px 5px;">46</li><li style="box-sizing: border-box; padding: 0px 5px;">47</li><li style="box-sizing: border-box; padding: 0px 5px;">48</li><li style="box-sizing: border-box; padding: 0px 5px;">49</li><li style="box-sizing: border-box; padding: 0px 5px;">50</li><li style="box-sizing: border-box; padding: 0px 5px;">51</li><li style="box-sizing: border-box; padding: 0px 5px;">52</li><li style="box-sizing: border-box; padding: 0px 5px;">53</li><li style="box-sizing: border-box; padding: 0px 5px;">54</li><li style="box-sizing: border-box; padding: 0px 5px;">55</li><li style="box-sizing: border-box; padding: 0px 5px;">56</li><li style="box-sizing: border-box; padding: 0px 5px;">57</li><li style="box-sizing: border-box; padding: 0px 5px;">58</li><li style="box-sizing: border-box; padding: 0px 5px;">59</li><li style="box-sizing: border-box; padding: 0px 5px;">60</li><li style="box-sizing: border-box; padding: 0px 5px;">61</li><li style="box-sizing: border-box; padding: 0px 5px;">62</li><li style="box-sizing: border-box; padding: 0px 5px;">63</li><li style="box-sizing: border-box; padding: 0px 5px;">64</li><li style="box-sizing: border-box; padding: 0px 5px;">65</li><li style="box-sizing: border-box; padding: 0px 5px;">66</li><li style="box-sizing: border-box; padding: 0px 5px;">67</li><li style="box-sizing: border-box; padding: 0px 5px;">68</li><li style="box-sizing: border-box; padding: 0px 5px;">69</li><li style="box-sizing: border-box; padding: 0px 5px;">70</li><li style="box-sizing: border-box; padding: 0px 5px;">71</li><li style="box-sizing: border-box; padding: 0px 5px;">72</li><li style="box-sizing: border-box; padding: 0px 5px;">73</li><li style="box-sizing: border-box; padding: 0px 5px;">74</li><li style="box-sizing: border-box; padding: 0px 5px;">75</li><li style="box-sizing: border-box; padding: 0px 5px;">76</li><li style="box-sizing: border-box; padding: 0px 5px;">77</li><li style="box-sizing: border-box; padding: 0px 5px;">78</li><li style="box-sizing: border-box; padding: 0px 5px;">79</li><li style="box-sizing: border-box; padding: 0px 5px;">80</li><li style="box-sizing: border-box; padding: 0px 5px;">81</li><li style="box-sizing: border-box; padding: 0px 5px;">82</li><li style="box-sizing: border-box; padding: 0px 5px;">83</li></ul>

相应的h头文件修改如下:

<code class="language-c hljs  has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background-position: initial initial; background-repeat: initial initial;"><span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* DO NOT EDIT THIS FILE - it is machine generated */</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#include <jni.h></span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifndef _Included_io_github_yanbober_ndkapplication_NdkJniUtils</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define _Included_io_github_yanbober_ndkapplication_NdkJniUtils</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifdef __cplusplus</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extern</span> <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"C"</span> {<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span>JNIEXPORT jstring JNICALL native_generate_key(JNIEnv *env, jobject obj, jstring name);<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifdef __cplusplus</span>}<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span><span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#endif</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li></ul>

对应的java文件中native方法名字换为映射表中的nativeGenerateKey即可。

以上代码不做详细解释,代码中有注释,同时可以参考该系列第二篇博客。

点我进入完整工程代码示例下载页面

总结

至此一个比Hello World稍微复杂一丁点儿的例子就分析的差不多了。整个JNI的基本雏形也就差不多这样子。下一篇会从其他角度来啃。T_T!!!

0 0
原创粉丝点击