在AS中进行 NDK 开发之前,我们先来简单的介绍几个大家都容易搞懵的概念:

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

  2. 何为“交叉编译”?

先看什么是 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协议在系统架构中处于什么位置:

在上图中,上层绿色的部分一般都是用 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++) 的动态库。

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

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

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

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

  • 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 编程实例

参考文献:

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

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

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

Android studio 下 JNI 开发实例的更多相关文章

  1. Android studio 下JNI编程实例并生成so库

    Android studio 下JNI编程实例并生成so库 因为公司需要为Android相机做美颜等图像后期处理,需要使用JNI编程,最近学了下JNI,并且在Android Studio下实现了一个小 ...

  2. Android Studio下jni应用

    最近在将一个小应用从eclipse开发迁移到android studio,程序中有native代码实现,在eclipse是靠Android.mk这么个mk文件来组织编译的,但到android stud ...

  3. Android studio 配置JNI环境

    Android studio配置jni开发环境,主要配置是两个build文件,以及新建一个jni文件,放c代码. 代码如下1: apply plugin: 'com.android.model.app ...

  4. 【android 开 发 】 - Android studio 下 NDK Jni 开发 简单例子

    Android 开发了一段时间,一方面 ,感觉不留下点什么.有点对不起自己, 另一方面,好记性不如烂笔头,为了往后可以回头来看看,就当做是笔记,便决定开始写博客.废话不多说 ! 今天想搞一搞 ndk ...

  5. Android studio 下 NDK Jni 开发 简单例子

    1. 创建一个新的工程 2. 创建一个新的类 JniText.java  点击Build--Make Project  后     选中工程 点击F4键 sdk location 中 Android ...

  6. android studio下的NDK开发详解(一)

    源地址:http://www.voidcn.com/blog/chengkaizone/article/p-5761016.html 好记性不如烂笔头,开始坚持写博客,学一点记一点,只为了生活更好. ...

  7. android下JNI开发

    android下JNI开发 what 什么是JNI JNI java native interface native本地 java本地接口 通过JNI可以实现java和本地代码之间相互调用 jni可以 ...

  8. 【Flutter 1-2】在 Windows 10下安装Flutter+Dart+Android Studio 配置Flutter开发环境

    在 Windows 10下安装Flutter+Dart+Android Studio 配置Flutter开发环境 文章首发地址 配置环境变量 由于部分网站被墙的原因,我们需要先配置Flutter国内镜 ...

  9. Android Studio C/C++开发环境配置

    Android Studio C/C++开发环境配置  我的开发环境 : Win 10 + android studio 1.5   一, 安装NDK 开发环境: 1.  Settings -> ...

随机推荐

  1. Lua5.2&Lua5.3中废除的方法

    Lua5.2和Lua5.3中居然把 table.getn() 废除了, webAdd = {"QQ", "BaiDu", "SMW"} fo ...

  2. GitHub上高质量项目

    scribejava/scribejava:一个简单的 Java 实现的 OAuth/OAuth2 库winterbe/java8-tutorial:绝对值得一看的Java8指南.教程javaee-s ...

  3. JavaScript基础视频教程总结(111-120章)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  4. CentOS 6下升级Python版本

    CentOS6.8默认的python版本是2.6,而现在好多python组件开始只支持2.7以上的版本,比如说我今天遇到的pip install pysqlite,升级python版本是一个痛苦但又常 ...

  5. Linux运行模式

    查看运行模式 cat /etc/inittab 0.表示关机模式,不要把默认模式设置成0 1.表示单用户模式 2.表示多用户模式 3.表示命令行模式 4.表示暂未被使用的模式,以后有可能会被使用 5. ...

  6. python学习,excel操作之xlsxwriter常用操作

    from datetime import datetime import xlsxwriter #打开文件 workbook = xlsxwriter.Workbook('Expenses03.xls ...

  7. window7 32位部署django

    window7 32位安装环境,所有的软件都用32位的,如果你想要用64位的软件需要操作系统和下面的相关软件都换成64位,我是在虚拟机上装了win7 32版做的测试. 软件下载地址: python2. ...

  8. Rabbitmq的使用及Web监控工具使用

    本文转载自:https://www.cnblogs.com/gossip/p/4475978.html windows安装手册请参考:http://www.rabbitmq.com/install-w ...

  9. 【Vue】谈Vue的依赖追踪系统 ——搞懂methods watch和compute的区别和联系

    从作用机制和性质上看待methods,watch和computed的关系 图片标题[原创]:<他三个是啥子关系呢?> 首先要说,methods,watch和computed都是以函数为基础 ...

  10. myEclipse配置jdk1.7

    第一步:下载jdk1.7 下载地址:http://download.csdn.net/download/chun201010/7824469 第二步:安装jdk1.7 将下载的压缩包进行解压,得到一个 ...