Android NDK开发技术与技巧总结与心得

Android NDK开发技术与技巧总结与心得

最近把JNI这玩意儿深度学习了下,之前虽然也做过,但都比较松散,没有系统的把整个知识框架和技术体系梳理过。网上也看了很多博文,基本说的都是环境配置然后一个Hello from jni的玩意儿,然后就没有然后了,基本很少有具体的应用以及对相应核心api的介绍,对其思想的介绍就更少了,所以想自己还是总结一篇出来,并不是为了装逼,就是怕过半年自己又全还给自己了(谁信啊)……

一、JNI到底是干嘛用的 
百度都能查到的官方解释我就不多说了。我自己的理解是两方面,一方面主要用于各种复杂算法的执行,C的效率高自不必说,更重要的是so的破解难度要远远大于apk了,很多公司就靠算法活着的,没有JNI用他们要死了~;另一方面,JavaNativeInterface,作为Java和C间的bridge,我们可以把很多java代码可能无法实现的事情丢到C层去搞。

二、怎么跑起来一个带JNI代码的工程 
我用的是AS2.1,主要学姿势,所以默认各位看官的环境已经折腾好,如果没折腾好百度一找一洗脸盆,学一下就好了。AS现在对NDK的支持还是很感人的low,不过编译很轻松,所以凑合着用吧。简单步骤总结下,不一样的同学不用喷,这不是重点:

1.写个类,写好自己需要的native方法,像这样:

<code class="hljs java has-numbering" style="display: block; padding: 0px; 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: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">package</span> com.amuro.ndkcompactdemo.chapter_1;

<span class="hljs-javadoc" style="color: rgb(136, 0, 0); box-sizing: border-box;">/**
 * Created by Amuro on 2016/6/23.
 */</span>
<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);">Chapter1JNI</span>
{</span>
    <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;">"ndk_compact"</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;">stringFromJNI</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> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">add</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> a, <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> b);
    <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;">addString</span>(String str);
    <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> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>[] <span class="hljs-title" style="box-sizing: border-box;">addArray</span>(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span>[] array);
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; 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; background-color: rgb(238, 238, 238);"><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>

2.写好之后,编译你的代码,保证通过。然后用AS自带的Termial进入工程根目录,我的根目录是这样的: 
D:\DevelopAS\workspace\NDKCompactDemo 
好,然后进入根目录的以下目录: 
D:\DevelopAS\workspace\NDKCompactDemo\app\build\intermediates\classes\debug 
然后输入命令javah -jni com.amuro.ndkcompactdemo.chapter_1.Chapter1JNI 
就这样会在刚才那个目录下生成一个.h头文件。不要问我javah是什么也不要问我h文件是什么,自己百度。生成头文件代码如下:

<code class="hljs vala has-numbering" style="display: block; padding: 0px; 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: transparent;"><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-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/* Header for class com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI */</span>

<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifndef _Included_com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#define _Included_com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI</span>
<span class="hljs-preprocessor" style="color: rgb(68, 68, 68); box-sizing: border-box;">#ifdef __cplusplus</span>
extern <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>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*
 * Class:     com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */</span>
JNIEXPORT jstring<span class="hljs-constant" style="box-sizing: border-box;"> JNICALL </span>Java_com_amuro_ndkcompactdemo_chapter_11_Chapter1JNI_stringFromJNI
  (JNIEnv *, jobject);

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*
 * Class:     com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI
 * Method:    add
 * Signature: (II)I
 */</span>
JNIEXPORT jint<span class="hljs-constant" style="box-sizing: border-box;"> JNICALL </span>Java_com_amuro_ndkcompactdemo_chapter_11_Chapter1JNI_add
  (JNIEnv *, jobject, jint, jint);

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*
 * Class:     com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI
 * Method:    addString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */</span>
JNIEXPORT jstring<span class="hljs-constant" style="box-sizing: border-box;"> JNICALL </span>Java_com_amuro_ndkcompactdemo_chapter_11_Chapter1JNI_addString
  (JNIEnv *, jobject, jstring);

<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">/*
 * Class:     com_amuro_ndkcompactdemo_chapter_1_Chapter1JNI
 * Method:    addArray
 * Signature: ([I)[I
 */</span>
JNIEXPORT jintArray<span class="hljs-constant" style="box-sizing: border-box;"> JNICALL </span>Java_com_amuro_ndkcompactdemo_chapter_11_Chapter1JNI_addArray
  (JNIEnv *, jobject, jintArray);

<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; 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; background-color: rgb(238, 238, 238);"><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></ul>

3.在工程的src/main目录下创建一个jni文件夹,把刚才生成的h文件复制过来。

4.对Gradle进行配置 
1)app module的build.gradle下增加ndk配置,完整gradle文件如下:

<code class="hljs rust has-numbering" style="display: block; padding: 0px; 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: transparent;">apply plugin: <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.android.application'</span>

android {
    compileSdkVersion <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">23</span>
    buildToolsVersion <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"23.0.2"</span>

    defaultConfig {
        applicationId <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"com.amuro.ndkcompactdemo"</span>
        minSdkVersion <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">15</span>
        targetSdkVersion <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">23</span>
        versionCode <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>
        versionName <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"1.0"</span>

        ndk{
            moduleName <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"ndk_compact"</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>
        }
    }

    buildTypes {
        release {
            minifyEnabled <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">false</span>
            proguardFiles getDefaultProguardFile(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'proguard-android.txt'</span>), <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'proguard-rules.pro'</span>
        }
    }
}

dependencies {
    compile fileTree(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">dir</span>: <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'libs'</span>, include: [<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'*.jar'</span>])
    testCompile <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'junit:junit:4.12'</span>
    compile <span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">'com.android.support:appcompat-v7:23.3.0'</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; 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; background-color: rgb(238, 238, 238);"><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></ul>

第一个是你自己的so库名称,第二个看注释,第三个复制粘贴就好 
2)在gradle.properties中添加: 
android.useDeprecatedNdk=true 
不要问我为什么。

5.针对之前生成的h文件,写自己的源文件,c或cpp均可,记得要把h文件include进来。

6.然后就可以编译执行啦,是不是好简单,AS帮我们把以前Eclipse需要的Android.mk文件的编写给自动化了。

三、用JNI你需要知道的一些知识

1.指针和引用 
Java把C最复杂的知识给封装了,而回到C的我们不得不把他们重新捡起来,记得我当年刚学也是被折腾的死去活来,其实想明白之后真的不复杂。如果你C的知识都还给谭老师了,建议你花个一两天先去捡回来,再回头搞会发现很多源码理解起来就轻松多了。这里举个例子,看代码: 
先定义一个结构体:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; 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: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> Student
{
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> age;
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">char</span>* name;
};</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; 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; background-color: rgb(238, 238, 238);"><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>

然后定义一个指针指向它:

<code class="hljs d has-numbering" style="display: block; padding: 0px; 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: transparent;"><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;">const</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">struct</span> Student* Stu;</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; 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; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li></ul>

然后我们要怎么用呢:

<code class="hljs coffeescript has-numbering" style="display: block; padding: 0px; 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: transparent;">func(Stu* student)
{
    printf<span class="hljs-function" style="box-sizing: border-box;"><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d"</span>, (*student)->age)</span>;
    <span class="hljs-title" style="box-sizing: border-box;">printf</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d"</span>, (**student).age)</span>;
}

<span class="hljs-title" style="box-sizing: border-box;">int</span> <span class="hljs-title" style="box-sizing: border-box;">main</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">()</span>
{
    <span class="hljs-title" style="box-sizing: border-box;">struct</span> <span class="hljs-title" style="box-sizing: border-box;">Student</span> <span class="hljs-title" style="box-sizing: border-box;">s</span> = {10, "<span class="hljs-title" style="box-sizing: border-box;">amuro</span>", 100};
    <span class="hljs-title" style="box-sizing: border-box;">Stu</span> <span class="hljs-title" style="box-sizing: border-box;">stu</span> = &<span class="hljs-title" style="box-sizing: border-box;">s</span>;

    <span class="hljs-title" style="box-sizing: border-box;">printf</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d"</span>, stu->age)</span>;
    <span class="hljs-title" style="box-sizing: border-box;">printf</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%d"</span>, (*stu).age)</span>;

    <span class="hljs-title" style="box-sizing: border-box;">func</span><span class="hljs-params" style="color: rgb(102, 0, 102); box-sizing: border-box;">(&stu)</span>;
}</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; 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; background-color: rgb(238, 238, 238);"><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>

运行结果四者打印出来的是10。大概解释一下: 
stu是s的指针,也就是说stu里是s的地址,所以stu在赋值时等号右边必须是一个地址值,而&就是取地址符。 
从指针中取值的时候有两种方法,代码里都写了。 
函数传递指针的时候要特别小心func的参数其实是一个指向stu的指针,也就是指针的指针。所以传值的时候应该把stu的地址传过去。而函数中的student保存的是stu的地址,所以*student才是取出stu地址里保存的s的地址,这时候就可以进行取值操作了。画个图大家就更好理解了:

这里写图片描述

举这个例子是为了方便大家理解JNIEnv *env这个玩意儿,其实我们自己函数中拿到的env就是刚才例子里的student,所以我们要调用env中的方法的时候,才会这样写:(*env)->MethodName

2.Java标准类和JNI接口的对应关系 
这里写图片描述 
这里写图片描述 
图我就直接盗了,反正口诀就是java类前面加个j就好了,然后首字母改小写。

3.jni.h在哪里 
很多帖子都会列一堆jni函数,看了一头雾水,其实只要我们找到源码,很多问题会非常容易解决。jni.h的源码在我们下载的NDK包里。我的地址是: 
D:\DevelopAS\asSDK\ndk-bundle\platforms\android-23\arch-arm\usr\include 
所有的方法都在里面了,然后根据需求去里面找就好了。 
文档的话看这里: 
http://docs.oracle.com/javase/7/docs/technotes/guides/jni 
看到这种密密麻麻的文档当时就想死了。

4.函数签名 
很重要,在C层反射的时候,都需要传这东西,百度能找到很多详细的帖子,我这里就取其精华了。 
这里写图片描述 
注意注意,这个表写错了,long的签名是J,我当时就被坑死了,查了好久才查出来! 
举个例子,比如 
public String doSomething(int a, String x, long[] b); 
签名就是:(ILjava/lang/String;[J)Ljava/lang/String; 
括号里是参数列表,注意非原生类一定要在前面加L后面加;,括号后面是返回值。分号一定不要忘记!!!! 
其实还能用javap命令来搞的,但是个人觉得还不如理解之后自己手写,熟悉之后效率高多了。这里就不赘述了。

5.JNIEnv是个啥 
1)JNIEnv是一个线程相关的结构体, 该结构体代表了 Java 在本线程的运行环境 ; 
2)JNIEnv 与 JavaVM : 注意区分这两个概念; 
– JavaVM : JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局只有一个; 
– JNIEnv : JavaVM 在线程中的代表, 每个线程都有一个, JNI 中可能有很多个 JNIEnv; 
3)JNIEnv 作用 : 
– 调用 Java 函数 : JNIEnv 代表 Java 运行环境, 可以使用 JNIEnv 调用 Java 中的代码; 
– 操作 Java 对象 : Java 对象传入 JNI 层就是 Jobject 对象, 需要使用 JNIEnv 来操作这个 Java 对象; 
4)JNIEnv 体系结构 
线程相关 : JNIEnv 是线程相关的, 即 在 每个线程中 都有一个 JNIEnv 指针, 每个JNIEnv 都是线程专有的, 其它线程不能使用本线程中的 JNIEnv, 线程 A 不能调用 线程 B 的 JNIEnv; 
– 当前线程有效 : JNIEnv 只在当前线程有效, JNIEnv 不能在 线程之间进行传递, 在同一个线程中, 多次调用 JNI层方法, 传入的 JNIEnv 是相同的; 
– 本地方法匹配多JNIEnv : 在 Java 层定义的本地方法, 可以在不同的线程调用, 因此 可以接受不同的 JNIEnv; 
5)JNIEnv 结构 : 由上面的代码可以得出, JNIEnv 是一个指针, 指向一个线程相关的结构, 线程相关结构指向 JNI 函数指针 数组, 这个数组中存放了大量的 JNI 函数指针, 这些指针指向了具体的 JNI 函数;

四、JNI要怎么用 
这个话题太大了,就我自己的总结来看,比较常用的其实无非就是四种,一种是操作基本类型,一种是String,一种是操作我们自定义的类型,一种就是数组。 
1.常用核心API接口 
妈蛋,做表格好蛋疼,就继续用截图代替吧…… 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述 
基本看到名字都能猜到意思了,有了方法名字,想要知道怎么用,传哪些参数的话,去查文档或者我刚才说的jni.h源文件就好啦。

2.还是回去用Eclipse吧 
用了一下AS之后真是觉得弱爆了,虽然编译方便了很多。要搞复杂的NDK开发还是要靠Eclipse,自己写Android.mk文件和Application.mk文件吧。 
下面总结下这两个文件的常用配置: 
这里写图片描述 
这里写图片描述 
这里写图片描述 
这里写图片描述

3.我能想到最痛苦的事,是没有代码提示~ 
记忆力拔群的手写代码大神请绕道。 
反正一开始用AS我是快疯了,基本没法写代码,写一行就要去查一下jni.h的头文件看看方法名和传入参数的定义,同时也不能调试,只能跑起来之后看日志或者报错,效率低下不能忍。那怎样让熟悉的代码提示出现呢,这里提供两个方法。 
方法一: 
1)你需要一个微软的VisualStudio,版本不必太高,我用的2008,建一个工程,选dll工程。 
2)从前面说的jni.h的头文件目录下,把jni.h和另一个叫jni_md.h的文件,加上你自己javah生成的头文件一起copy到studio项目目录下,像这样: 
这里写图片描述 
然后在你的工程里把这些头文件导入: 
这里写图片描述 
然后自动提示就出来啦啦啦啦~ 
这里写图片描述 
也没有要死的各种红线绿线。 
3)编译出的dll文件其实是可以拿来用的(学过操作系统的童鞋都知道,dll动态链接库其实就是windows上的so),这里有一个注意点,就是如果你安装的是64位的java,记得一定要把VS的编译条件改成x64,不然编译出来的dll文件java是没法解析的。把你生成的dll文件的路径配置到环境变量的PATH下面,然后写一个java程序就能用load这个dll啦。我的配置是这样的: 
D:\VisualStudio\Projects\NDKTest\x64\Debug;(这里面有个NDKTest.dll文件) 
4)写个Java程序去测试这个JNI代码吧,System.loadLibrary的时候,lib名就传“NDKTest”就OK了。这个适合测试算法类的JNI,测试成功后直接拿到Android程序里用就好了。毕竟对C的支持,VS是无出其右的。

方法2:Eclipse增加代码提示 
1)我相信这个才是很多童鞋想要的~为了把这个搞出来花了我一整天折腾各种环境和配置,Orz 
2)确认你的eclipse安装了cdt,如果没有,去官网找下载地址去,我的是这里: 
http://download.eclipse.org/tools/cdt/releases/juno 
注意这个地址是在eclipse的install new software中使用的,不要直接网页打开。 
3)下载MinGW,安装MinGW,配置MinGW,请看教程: 
http://blog.csdn.net/hujingn/article/details/5849516 
4)新建C项目,选择如图,记得右边一定要选MinGW 
这里写图片描述 
5)好了,出现了我们熟悉的代码界面,stdio.h,stdlib.h都能看到源码了,爽,但是这时候并没有jni的源码,别急,右键项目,把ndk-bundle里源码的地址配置进去: 
这里写图片描述
具体看图,我就不贴了。配置完成确认OK。 
6)激动人心的时候终于到了,加上jni.h的include,看到变蓝色没,变就对了。这时候我们就可以直接F3啦~然后,我们就可以把我们的javah生成的头文件拷过来了,然后的然后请看图: 
这里写图片描述
这下想看什么源码,都轻松愉快了,有些小算法,还可以在下面的main函数里自己测试。

差不多就是这些东西了,剩下的就是学习各种API的使用了,作为程序员这个是必备技能了。跪求谷歌尽快完美支持NDK开发,这样折腾实在是太痛苦了。如果各位有更好更优秀的方式,欢迎留言讨论~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值