Android studio 下 JNI 开发实例 将应用代码由eclipse导入Android studio的方法NDK-Build和Cmake两种方法(以android_serialport_ap

 在AS中进行 NDK 开发以前,咱们先来简单的介绍几个你们都容易搞懵的概念:html

  1.  到底什么是JNI,什么是NDK?java

  2.  何为“交叉编译”?android

    先看什么是 JNI?JNI 的全称就是 Java Native Interface,即java本地开发接口。可能你们和我同样,一听到接口什么的就犯懵:“我也知道这是java本地开发接口的意思,但它具体是个什么意思我仍是搞不明白。”其实JNI它就是一种协议,一说协议,那它就是对某种东西的一个规范和约束,说的好听一点就是标准化。若是你想用我这个东西,那你必需要遵照我这边的规范。像http协议同样,http做为超文本传输协议,它规范了咱们上网时从客户端到服务器端等一系列的运做流程。正由于如此,咱们才能畅通无阻的上网。那么换作JNI也同样,只不过JNI这个协议是用来沟通java代码和外部的本地代码(c/c++)。也就是说有了JNI这个协议,咱们才可以随意的让java代码调用C/C++的代码,一样C/C++的代码也能够调用java的代码。若是没有这个协议做为支撑,那么java和C/C++代码想要相互调用是不可能的。下面经过两个图简单看一下JNI协议在系统架构中处于什么位置:c++

                                     

    在上图中,上层绿色的部分通常都是用 Java 代码写的,下层橘黄色的部分通常都是用 C/C++ 代码写的。能够看出,正式因为有了中间 JNI 的存在咱们才能够在 Application 层经过 JNI 调用下层中的一些东西。了解了JNI 的概念后,咱们再看看 NDK,NDK(Native Development Kit)就比较好理解了,它就是一个本地开发的“工具包”。Java 开发要用到 JDK,Android 开发要用到 SDK,那咱们在 Android 中要进行 native 开发,也要用到它对应的工具包,即 NDK。通俗的来说,NDK 就是帮助咱们能够在Android应用中使用 C/C++ 来完成特定功能的一套工具。 NDK的做用有不少,咱们简单的列举两个,好比:编程

        1.  首先 NDK 能够帮助开发者“快速”开发 C(或C++) 的动态库。api

        2.  其次,NDK 集成了“交叉编译器”。使用 NDK,咱们能够将要求高性能的应用逻辑使用 C 开发,从而提升应用程序的执行效率。服务器

    上面提到了“交叉编译”,咱们最后再解释一下什么是交叉编译。你们都知道编译器在将中间代码链接成当前计算机可执行的二进制程序时,链接程序会根据当前计算机的 CPU、操做系统的类型来转换。而根据运行的设备的不一样,CPU 的架构也是不一样,大致有以下三种常见的 CUP 架构:架构

  • arm 结构 :主要在移动手持、嵌入式设备上。咱们的手机几乎都是使用的这种 CUP 架构。app

  • x86 结构 : 主要在台式机、笔记本上使用。如 Intel 和 AMD 的 CPU 。eclipse

  • MIPS 架构:多用在网关、猫、机顶盒等设备。

    若想在使用了基于 x86 架构 CPU 的操做系统上编译出能够在基于 arm 结构 CPU 的操做系统上运行的代码,就必须使用交叉编译。因此综上所述:交叉编译就是在一个平台下(好比:CPU 架构为 X86,操做系统为 Windows)编译出在另外一个平台上(好比:CPU 架构为 arm, 操做系统为 Linux)能够执行的二进制代码。Google 提供的 NDK 就能够完成交叉编译的工做。
好了,上面的基本概念介绍完之后,咱们正式进入 AS 下 NDK 开发的讲解。
 

准备工做

首先,你须要把 NDK 相关的下载下来。以下图所示,红色框选中的都是开发中须要用到的。

  • NDK:经过 NDK-build 方法来使用本地库

  • CMake:经过 CMake 方法来使用本地库

  • LLDB:用来调试 C/C++ 的工具

 

编写代码

 配置好 NDK 开发环境以后,在项目的布局文件添加一个 TextView,经过调用底层本身写好的 C/C++ 代码来返回一个字符串,最后呈如今 TextView上。 

具体代码内容以下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView textView = findViewById(R.id.text); textView.setText(JNIUtils.getString());
    }
}

其中的 JNIUtils 下面立刻就会提到。接下去,在 MainActivity 同级,新建一个类,包含一个 native 方法。

public class JNIUtils {
   public static native String getString(); }

 可是,会发现,方法名是飘红的,说明尚未被识别:

 

把鼠标放到上面,它会提示咱们对应的JNI头文件没有查找到。那么接下来咱们要作的就是去生成与这个 getString() 方法所对应的头文件。

生成 .h 头文件

在 AS 自带的 Terminal 命令行窗口中输入以下几条指令,回车:

cd app
cd src/main/java
javah -classpath . -jni com.example.shenjiaqi.myapplication.JNIUtils

使用 javac 命令将 JNIUtils.java 进行编译,而后使用 javah -jni 命令编译获取 jni 所须要的头文件。

这里咱们采用以下命令:

// javah -classpath . -jni 包名.类名。
javah -classpath . -jni com.example.shenjiaqi.myapplication.JNIUtils

注意编译命令必定得在 java 目录下下运行。编译成功没有遇到坑的话,你就能够在 ···\src\main\java 目录下看到一个.h文件。

接下来在项目中建立一个 jni 目录,并将刚生成的 .h 文件剪切至这个目录:

咱们先来查看一下这个 .h 文件的内容。这里面用 java 的概念来讲就至关于接口内的抽象方法,须要咱们建立 .c 文件来实现这些方法同时也就将咱们的定义的 native 方法实现了:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_shenjiaqi_myapplication_JNIUtils */

#ifndef _Included_com_example_shenjiaqi_myapplication_JNIUtils
#define _Included_com_example_shenjiaqi_myapplication_JNIUtils
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_shenjiaqi_myapplication_JNIUtils
 * Method:    getString
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_shenjiaqi_myapplication_JNIUtils_getString
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

接着,新建一个 c++ 的文件,在 jni 目录下建立一个 JNIHello.cpp 文件来实现 .h 文件中的抽象方法:

#include "com_example_shenjiaqi_myapplication_JNIUtils.h"
JNIEXPORT jstring JNICALL Java_com_example_shenjiaqi_myapplication_JNIUtils_getString
        (JNIEnv *env, jclass jclass){
return env->NewStringUTF("Hello World From JNI!!!!!");
}

能够看到咱们首先须要把原来生成的 JNIUtlis 对应的头文件引入进来,下面的代码基本都是从 com_example_shenjiaqi_myapplication_JNIUtils.h 中复制粘贴过来的一部分,而后稍加修改。修改的地方主要有 getString 的两个参数和里面的简单实现,参数方面就是加了 env 和 jclass 两个字段。函数里面的实现呢,就是简单的返回一个字符串 “Hello World From JNI!!!!!” , 你们如今就须要知道若是要在这里返回一个字符串就必需要经过 env->NewStringUTF("xxxxxx"); 这行代码

目录结构以下图:

NDK 配置

接下来咱们在 build.gradle 中添加 ndk 配置:

运行项目了,发现报错:

不要慌,说是让咱们采用 CMake 或者 ndk-build 方式来捷成。这时候咱们打开 build 目录,以下图:

其中,有个文件叫作 Android.mk ,须要这个来为咱们生成 .so 文件,操做步骤以下,先把目录切换到 Android 视角下,否则会没有 Link C++ Project with Gradle 这个选项的 :

 在弹窗中选择 ndk-build ,找到以前说的 Android.mk 这个文件。

这时候,咱们再回到 JNIUtils.java ,发现没有飘红了。可是运行编译后会出现错误提示:

说是没有找到 getString()的实现方法。在 JNIUtils 中添加以下代码,便可解决上面的问题。能够发如今 build 中已经生成相应的 .so 文件了。

public class JNIUtils {
    static {
        System.loadLibrary("JNIHello");
    }
    public static native String getString();
}

 再次编译,运行成功:

        

 

demo 地址: Android jni 编程实例

 

参考文献:

一、将应用代码由eclipse导入Android studio的方法NDK-Build和Cmake两种方法(以android_serialport_api为例)

二、NDK开发(一)————如何在Android Studio下进行NDK开发

三、使用AndroidStudio编写第一个JNI程序

相关文章
相关标签/搜索