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. 洛谷 P2978 [USACO10JAN]下午茶时间Tea Time

    P2978 [USACO10JAN]下午茶时间Tea Time 题目描述 N (1 <= N <= 1000) cows, conveniently numbered 1..N all a ...

  2. 怎样让索引仅仅能被一个SQL使用

    有个徒弟问我,要创建一个索引,去优化一个SQL,可是创建了索引之后其它 SQL 也要用 这个索引,其它SQL慢死了.要优化的SQL又快.遇到这样的问题咋搞? 一般遇到这样的问题还是非常少的.处理的方法 ...

  3. 谈谈Command对象与数据检索

    摘要 到目前为止,我相信大家对于ADO.NET如何与外部数据源建立连接以及如何提高连接性能等相关知识已经牢固于心了.连接对象作为ADO.NET的主力先锋,为用户与数据库交互搭建了扎实的桥梁.它的一生是 ...

  4. Mongodb总结3-稍微封装一下

    上次发表的2,纯粹是Demo,演示API的用法. 今天,稍微封装了下,看得更清楚. 考虑到不容易做得很有通用性,所以封装的一般,换种场景需要直接修改代码,但是有一部分是可以复用的. 最近项目,很可能只 ...

  5. Servlet的异常处理机制

    一 声明式异常处理   在web.xml中对声明对各种异常的处理方法. 通过 <error-page>元素来声明. 此元素的结构如下:                    +------ ...

  6. programming+windows+MFC

    1)CMyApp declares no data members 2)CWinApp::InitInstance run after application build but before the ...

  7. ECMALL功能拓扑图以及模式分析

    ECMALL  VS  常规的B2C产品(以ECSHOP做对比)的区别: 1.支持多用户在同一个域名下开店. 2.开店的卖家各自结算,直接收钱.平台只是提供了一个类似传统行业的摊位.平台不过手金钱 3 ...

  8. 洛谷 P1458 顺序的分数 Ordered Fractions

    P1458 顺序的分数 Ordered Fractions 题目描述 输入一个自然数N,对于一个最简分数a/b(分子和分母互质的分数),满足1<=b<=N,0<=a/b<=1, ...

  9. [RxJS] How To get the results of two HTTP requests made in sequence

    switchMap can chain two HTTP requests together, creating one request based on the results of the fir ...

  10. 关于C++中用两个迭代器方式初始化string的知识

    string(iter1, iter2); 第一点:两个迭代器必须指向同一个容器. 第二点:iter2必须>=iter1. 第三点:假设iter1等于iter2,那么结果为空[] 另外一个比較特 ...