1. NDK简介
Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK”

1.1 NDK产生的背景
Android平台从诞生起,就已经支持C、C++开发。
众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言。
但这并不等同于“第三方应用只能使用Java”。
在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,
也就是第三方应用完全可以通过JNI调用自己的C动态库,
即在Android平台上,“Java+C”的编程方式是一直都可以实现的。

不过,Google也表示,使用原生SDK编程相比Dalvik虚拟机也有一些劣势,Android SDK文档里,找不到任何JNI方面的帮助。
即使第三方应用开发者使用JNI完成了自己的C动态链接库(so)开发,但是so如何和应用程序一起打包成apk并发布?
这里面也存在技术障碍。比如程序更加复杂,兼容性难以保障,无法访问Framework API,Debug难度更大等。
开发者需要自行斟酌使用。
 
于是NDK就应运而生了。NDK全称是Native Development Kit。
NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。
NDK将是Android平台支持C开发的开端。

1.2 为什么使用NDK
    1.代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反编译难度较大。
    2.可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
    3.提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
    4.便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。

1.3 NDK简介
1.NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件
(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
 
2.NDK提供了一份稳定、功能有限的API头文件声明
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。
从该版本的NDK中看出,这些API支持的功能非常有限,
包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。

1.4 NDK的安装
见《 Ubuntu14.04下最新Android NDK安装 》

1.5 NDK的目录结构说明
 . build:    该目录存放的使用NDK的mk脚本,mk脚本指定了编译参数
 . docs:     该目录存放的是NDK的使用帮助文档
 . platforms:这里面存放的是与各个Android版本相关的平台(x86,arm,mips)相关C语言库和头文件
 . prebuilt: 预编译工作目录
 . samples:  存放的是演示程序
 . sources:  存放的是NDK工具链的C语言源码
 . tests:    测试相关的文件
 . toolchains:工具链,存放了三种架构的静态库等文件
 . ndk-build.cmd:Window平台使用NDK的命令
 . ndk-build:Linux平台使用NDK的命令

2. JNI入门
2.1 新建一个Android工程
这一步很简单,只需要命名下工程的名字,一路向下即可,最后编译并运行测试下;

2.2 修改 MainActivity.java文件
修改 app->src->main->java->MainActivity,
定义一个native方法:

...
public class MainAcitivity extends AppCompatActivity {
  // 新定义的native方法,意思是该方法的具体实现交给c语言实现
  public native String helloC();

...

2.3 创建jni目录及文件
切换到“Project”视图,
在 app->src->main下,右键选择“New->Directory”
填入目录名"jni", 并点击"OK".
在jni目录下创建hello.c源文件,代码清单如下:

// 引入头文件
#include <stdio.h>
#include <jni.h>
#include "hello.h"

// 定义在MainActivity.java类中的helloC对应的C语言函数
jstring Java_com_example_luis_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj){
  char* str = "hello from C";

// 调用 jni.h中定义的创建字符串函数
  jstring string = (*(*env)).NewStringUTF(env, str);
  return string;
}

NOTE:
上面的代码虽然简单但是关于jni.h头文件和方法名必须单独说明;

JNI中C源文件方法名的命名规则
这里的命名规则指用于跟java文件中native方法对应的C语言方法,而C语言中的其他方法命名只要符合C语言规则就行。

jstring Java_com_example_luis_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj) 中,
 . jstring是方法返回值类型,我们可以把jstring看成是java中String跟C语言中char*类型的一个中间转换类型,
   java跟C语言的数据类型是不一样的,他们之间要想互相调用就必须通过一种中介来实现,这个中介就是在jni.h头文件中定义的。

. 关于更多的转换类型,在本文档的第2章会有更详细的说明。

. 方法名第一个字母必须是Java,首单词大写,然后下划线_,
    然后是将该方法所在的包、类、方法用“_”连接起来,比如com.sample.luis.jnihello.MainActivity类中的helloC方法,
    转变成C语言中的方法名为Java_com_sample_luis_jnihello_MainActivity_helloC。
    方法的形参有两个是必须的也就是不管java中的方法是否有形参,但是C语言中对应的方法必须有JNIEnv* env,和jobject obj,
    如果java方法中还用其他形参,那么在C语言中严格按照顺序排在jobject obj参数的后面即可。

. 上面的env代表指向JVM的指针,obj是调用该方法的java对象。

2.4 使用NDK编译生成hello.so文件
从终端进入jni目录:
$ cd jni
$ ls
hello.c
hello.h

从NDK安装目录的sample/hello-jni/jni目录复制文件
$ cp /opt/android-ndk-r10e/samples/hello-jni/Application.mk ./
$ cp /opt/android-ndk-r10e/samples/hello-jni/Android.mk  ./

再在Android中修改Android.mk
修改后的Android.mk文件清单如下,
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c

include $(BUILD_SHARED_LIBRARY)

我们只需要修改LOCAL_MODULE和LOCAL_SRC_FILES两个参数即可。
LOCAL_MODULE参数是指定编译后的目标文件的名称,其实编译好的目标文件名为libhello.so,
LOCAL_SRC_FILES指定了要编译的源文件。

还可以通过修改Application.mk文件来指定生成的动态库的类型:
如按以的修改则只会生成一种动态库:
# Build both ARMv5TE and ARMv7-A machine code.
APP_ABI := armeabi x86

也可所设置成生成所有平台都支持的动态库:
APP_ABI :=all

在终端运行命令:
$ ndk-build

命令运行后,它会

2.4 修改jni的库目录 
将app->src->main->libs改成
app->src->main->jniLibs

NOTE:
每次运行后ndk-build后,都需要修改这个目录名,否则对动态库的修改不会生效;

2.5 修改 gradle->gradle.properties
在文件的最末行添加:
android.useDeprecateNdk=true

2.6 配置项目NDK目录
选择菜单
File->Project Structure->Android NDK location:
填入NDK的安装路径;

2.7 在MainActivity.java中调用 C语言
代码清单如下:

...
public class MainAcitivity extends AppCompatActivity {
  // 新定义的native方法,意思是该方法的具体实现交给c语言实现
  public native String helloC();
  
  // 加载libhello.so动态库,但是我们在加载时必须去掉lib和后缀
  static {
    System.loadlibrary("hello");
  }

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

// 调用并显示
    TextView tv = new TextView(this);
    tv.setText(helloC());
    setContentView(tv);
  }

...

3. 编译运行
运行后,会显示:

hello from C

表示测试成功!

参考文档:
http://bbs.itheima.com/thread-189661-1-1.html

AndroidStudio实现JNI的示例详解的更多相关文章

  1. jquery移除、绑定、触发元素事件使用示例详解

    这篇文章主要介绍了jquery移除.绑定.触发元素事件使用示例详解,需要的朋友可以参考下. unbind(type [,data]) //data是要移除的函数 $('#btn').unbind(&q ...

  2. gcc与g++的编译链接的示例详解

    一.编译方式的示例详解 1. 编译C代码 代码如下:main.c /*!  ************************************************************** ...

  3. 史上最易懂——ReactNative分组列表SectionList使用详情及示例详解

    React Native系列 <逻辑性最强的React Native环境搭建与调试> <ReactNative开发工具有这一篇足矣> <解决React Native un ...

  4. Spring Boot 2.x 快速入门(下)HelloWorld示例详解

    上篇 Spring Boot 2.x 快速入门(上)HelloWorld示例 进行了Sprint Boot的快速入门,以实际的示例代码来练手,总比光看书要强很多嘛,最好的就是边看.边写.边记.边展示. ...

  5. VS2010 Chart控件(一)Chart控件在ASP.NET网站中的应用示例详解(C#语言)

    步骤如下: 1. Chart控件(一)Chart控件在ASP.NET网站中的应用示例详解(C#语言)" title="VS2010 Chart控件(一)Chart控件在ASP.NE ...

  6. socket编程的同步、异步与阻塞、非阻塞示例详解

     socket编程的同步.异步与阻塞.非阻塞示例详解之一  分类: 架构设计与优化 简介图 1. 基本 Linux I/O 模型的简单矩阵 每个 I/O 模型都有自己的使用模式,它们对于特定的应用程序 ...

  7. Android JNI作用及其详解

    Android JNI作用及其详解 Java Native Interface (JNI)标准是Java平台的一部分,它允许Java代码和其他语言写的代码进行交互.JNI 是本地编程接口,它使得在 J ...

  8. String.format()【示例详解】

    String.format()[示例详解] 整理者:Vashon 前言: String.format 作为文本处理工具,为我们提供强大而丰富的字符串格式化功能,为了不止步于简单调用 String.fo ...

  9. SpringBoot与PageHelper的整合示例详解

    SpringBoot与PageHelper的整合示例详解 1.PageHelper简介 PageHelper官网地址: https://pagehelper.github.io/ 摘要: com.gi ...

随机推荐

  1. asp.net 前后台数据交互方式(转)

    https://blog.csdn.net/luckyrass/article/details/38758007 一.前台直接输出后台传递的数据 后台代码: // .aspx.cs public st ...

  2. Spring学习总结(6)——Spring之核心容器bean

    一.Bean的基础知识 1.在xml配置文件中,bean的标识(id 和 name) id:指定在benafactory中管理该bean的唯一的标识.name可用来唯一标识bean 或给bean起别名 ...

  3. 洛谷——V1772 巧妙填数

    描述 将1,2,\cdots,91,2,⋯,9共99个数分成三组,分别组成三个三位数,且使这三个三位数构成1:2:31:2:3的比例. 试求出所有满足条件的三个三位数.例如:三个三位数192,384, ...

  4. activity-栈相关属性

    1.启动任务栈 第一种,动作设置为“android.intent.action.MAIN”,类别设置为“android.intent.category.LAUNCHER”,可以使这个ACT(activ ...

  5. solr索引创建流程

    solr索引创建流程: 分词组件Tokenizer 分词组件(Tokenizer)会做以下几件事情(这个过程称为:Tokenize),处理得到的结果是词汇单元(Token). 1.将文档分成一个一个单 ...

  6. 【AtCoder Regular Contest 082 F】Sandglass

    [链接]点击打开链接 [题意] 你有一个沙漏. 沙漏里面总共有X单位的沙子. 沙漏分A,B上下两个部分. 沙漏从上半部分漏沙子到下半部分. 每个时间单位漏1单位的沙子. 一开始A部分在上面.然后在r1 ...

  7. How to Rotate Tomcat catalina.out

    If catalina.out becomes 2GB in size, tomcat crashes and fails to start without any error message. To ...

  8. 【微信】微信获取TOKEN,以及储存TOKEN方法,Spring quartz让Token永只是期

    官网说明 access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.开发人员须要进行妥善保存. access_token的存储至少要保留512个字符空间.ac ...

  9. Day1:第一个python小程序

    Day1:第一个python小程序与开发工具Pycharm 一.Hello World C:\Users\wenxh>python Python 3.6.2 (v3.6.2:5fd33b5, J ...

  10. DC中为什么要用Uniquify?

    转自:http://blog.sina.com.cn/s/blog_68c493870101exl7.html 为了在layout中进行时钟树的综合,网表在DC中必须被uniquified.所谓uni ...