JNI在Android Studio2.2中基本配置和使用

MackGoforth 8年前
   <ul>     <li> <h3><sub><strong>what?</strong></sub></h3>      <ol>       <li> <p>JNI<br> Java Native Interface 它允许Java代码和其他语言写的代码进行交互。JNI 是本地编程接口,它使得在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行交互操作。<br> 由于Android的应用层的类都是以Java写的,这些Java类编译为Dex型式的Bytecode之后,必须靠Dalvik虚拟机(VM: Virtual Machine)来执行。</p> </li>       <li> <p>NDK<br> Native Develop Kit(本地开发工具包),类似于JDK;只是一套工具,它可以帮助开发者在android开发中,它使用的是JNI机制.</p> </li>       <li> <p>两者的区别<br> JNI Java Native Interface <strong>java调用本地接口 的技术名词</strong><br> NDK Native Developer Kit <strong>谷歌给开发人员的工具包</strong></p> </li>      </ol> </li>    </ul>    <ul>     <li> <h3><strong>使用ndkBuilder进行项目的构建</strong></h3> </li>     <li> <p>下载ndk(已装,略过)</p> </li>    </ul>    <p style="text-align:center"><img src="https://simg.open-open.com/show/752720711766cf9c45728d69686a5ab9.png"></p>    <p style="text-align:center">ndk下载.png</p>    <p> </p>    <p><strong>进入项目设置界面</strong></p>    <p><img src="https://simg.open-open.com/show/5200bdb4edcff334132eee00639c7af1.png"></p>    <p style="text-align:center">20160531123154969.png</p>    <p><strong>没有安装的话,直接Download即可,PS.在最终安装的时候会卡很长时间,耐心等待安装完成</strong></p>    <ol>     <li> <p>配置app,build.gradle文件</p>      <ol>       <li>在app的build.gradle中,也就是要运行的项目中的build.gradle文件中的defaultConfig节点中增加 <pre>  <code class="language-cpp">ndk {       moduleName "NdkJniDemo"   //生成的so名字       ldLibs "log", "z", "m"    //添加依赖库文件,因为有log打印等//非必填加项       abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无。//不填写则生成所有   }</code></pre> </li>      </ol> </li>    </ol>    <ol>     <li> <p>创建本地需要创建调用C的代码,这里使用一个获取字符串的方法为例</p> </li>     <li> <p>新建一个工具类JniUtils,使用C获取一个字符串,然后展示到一个</p> <pre>  <code class="language-cpp">public class JniUtils {    public static native String getStringFormC();    ...可以有很多的native代码  }</code></pre> 使用native关键字,表示调用本地的方法,该方法可以使用C/C++语言来实现</li>    </ol>    <ol>     <li> <p>生成.h,C/C++的头文件(熟悉C的知道,可以没有头文件,头文件只是定义类中所有方法(C中没有类的概念))</p>      <ol>       <li> <p>build或rebuild或clear一下程序之后,会在build/intermediates/classes/debug目录中生成项目中的所有的class文件,</p> </li>      </ol> </li>    </ol>    <p style="text-align:center"><img src="https://simg.open-open.com/show/24069fd45202027ab0bbe736c0d28c8d.png"></p>    <p style="text-align:center">class文件路径.png</p>    <pre>  <code class="language-cpp">2. 命令行进入debug目录cd <路径>  3. 编译指令</code></pre>    <pre>  <code class="language-cpp">javah -jni com.wobiancao.ndkjnidemo.ndk.JniUtils</code></pre>    <p>注意 这里javah -jni后面跟的是JniUtils类的全路径,如果javah报不存在之 类的,是你的java环境没有配置好。</p>    <ol>     <li> <p>编译过后会在debug目录下生成一个.h的文件,它的命名方式会很长,基本 是全路径的命名方式<br> jonathanhsia_com_ndktest_utils_JniUtils.h</p> </li>     <li> <p>拷贝文件到项目中的main/jni目录下,如果没有直接创建即可</p> </li>    </ol>    <ol>     <li> <p>编写C的方法实现</p>      <ol>       <li> <p>在jni目录下新建C/C++文件,引用头文件,复写其中的java要调用的C/C++方法,返回一个字符串,</p> </li>      </ol> </li>     <li> <p>在JniUtils中静态导入C/C++所生成的so包</p> <pre>  <code class="language-cpp">static {       System.loadLibrary("NdkJniDemo");//之前在build.gradle里面设置的so名字,必须一致   }</code></pre> <h3><strong>此时run之后java代码即可以调用到用C/C++实现的代码了</strong></h3> </li>    </ol>    <p>"PS. 在run之后,会在build/intermediates/ndk/debug/lib目录下会出现在build.gradle中配置的三中cpu架构的so包,此时删除jni目录中的源码,将这些so包直接的拷入到项目中就可以直接的使用了"</p>    <p><strong>遇到的坑</strong></p>    <ol>     <li>C/C++不能格式化代码,否则会出现,编译通过(可能编译出错,但是没有阻止运行),运行不了,报出找不到so包的异常</li>     <li>首次run的时候可能报出 <pre>  <code class="language-cpp">Error: NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin. For details, see http://tools.android.com/tech-docs/new-build-system/gradle-experimental. Set "android.useDeprecatedNdk=true" in gradle.properties to continue using the current NDK integration.</code></pre> 这样的错误,按照里面的提示在gradle.properties文件中增加android.useDeprecatedNdk=true字段即可</li>     <li><strong>不支持intant run</strong></li>     <li>两个c同时实现h中的方法,会报错,不允许,逻辑上也是不允许;</li>     <li>JniUtils的位置不能够随便已经,因为和C/C++文件中是一一对应的引用关系</li>    </ol>    <h3><strong>使用cmake进行项目的构建</strong></h3>    <pre>  <code class="language-cpp">* **1.cmake编辑功能是android studio 2.2才支持的新功能;目的是简化jni的开发过程,使用studio2.2新建项目的话,会有相应的让你勾选使用cmake**</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/56a6744ccaec73f29f61d474dd87a4a0.png"></p>    <p style="text-align:center">1474439172560.png</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b41708cf0e08a90eab2e43939f743677.png"></p>    <p style="text-align:center">1474439229866.png</p>    <ul>     <li> <p><strong>1.当勾选了include C++ Support时,在创建项目的时候,会多出如上界面,选择C/C++的标准,此处的设置在app的build.gradle中的defaultConfig会增加设置</strong></p> <pre>  <code class="language-cpp">externalNativeBuild {            cmake {                cppFlags "-frtti -fexceptions"//这个标记是第一个选项,如果使用C++11的标准,则使用                //cppFlags "-std=c++11"            }        }</code></pre> </li>    </ul>    <ul>     <li> <p><strong>2.在build.gradle中的android节点下面会增加配置,指定生成so文件配置文件的路径</strong></p> <pre>  <code class="language-cpp">externalNativeBuild {        cmake {            path "CMakeLists.txt"        }    }</code></pre> </li>    </ul>    <ul>     <li> <p><strong>3.创建需要调用C/C++代码的java代码,和ndkBuilder相同</strong></p> <pre>  <code class="language-cpp">public class JniUtils {    public static native String getStrFromC2();  }</code></pre> </li>    </ul>    <ul>     <li><strong>4.在项目中src/main/中创建cpp目录,里面可以直接的创建cpp源代码,和ndkBuild一样,用C/C++所写的源代码中的方法名称必须是全路径的方法名,然后以Java开头,分割使用下划线.</strong></li>    </ul>    <pre>  <code class="language-cpp">#include <jni.h>  #include <string>    extern "C"  jstring  Java_com_ndkcmaketestapp_utils_JniUtils_getStrFromC2(JNIEnv *env, jobject thiz) {      std::string hello = "Hello from C++ Two!";      return env->NewStringUTF(hello.c_str());  }</code></pre>    <ul>     <li> <p><strong>5.CMakeLists.txt文件中的具体配置</strong></p> <pre>  <code class="language-cpp">cmake_minimum_required(VERSION 3.4.1) #指定cmake版本  add_library(form SHARED src/main/cpp/form.cpp) #hello是生成的so文件的名称,要和cpp文件的名称相同  target_link_libraries(hello log android) # 此处增加了,日志的链接库</code></pre> </li>    </ul>    <ul>     <li> <p><strong>6.在java代码中增加引用so库的代码,使代码生效</strong></p> <pre>  <code class="language-cpp">public class JniUtils {      // Used to load the 'native-lib' library on application startup.    static {        System.loadLibrary("form");//此处的form库的名称需要和CMakeLists.txt中配置的相同    }    public static native String getStrFromC2();  }</code></pre> </li>    </ul>    <ul>     <li> <p><strong>7.在run成功之后,会在build目录的上方增加.externalNativeBuild目录,其中.externalNativeBuild/cmake/debug/obj包含所有生成的so包,同样的拷贝到项目中的jniLibs就可以直接的使用</strong></p> <p>CMake的优势</p>      <ul>       <li>1.可以直接的在C/C++代码中加入断点,进行调试</li>       <li>2.java引用的C/C++中的方法,可以直接 ctrl+左键 进入</li>       <li>3.对于include的头文件,或者库,也可以直接的进入</li>       <li>4.不需要配置命令行操作,手动的生成头文件,不需要配置 android.useDeprecatedNdk=true 属性</li>      </ul> </li>    </ul>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/3cc045829bb6</p>    <p> </p>