一:概述
Android的开发主要是基于java语言进行开发的,所以导致了用Android SDK进行开发的工程师们都必须使用Java语言。但是Android 平台从一开就已经支持了C/C++了,Google从一开始就说明Android也支持JNI编程方式,也就是第三方应用完全可以通过JNI调用自己的C动态库。
二:NDK
1.什么是ndk
ndk官网的描述如下
关键字: Android NDK 是一套允许您使用原生代码语言(例如C和C++) 实现部分应用的工具集。在开发某些类型应用时,这有助于您重复使用以这些语言编写的代码库
简单来说: Android NDK 就是一套工具集合,允许你使用C/C++语言来实现应用程序的部分功能
NDK 是Native Develop Kit的含义,从含义很容易理解,本地开发。大家都知道,Android 开发语言是Java,不过我们也知道,Android是基于Linux的,其核心库很多都是C/C++的,比如Webkit等。那么NDK的作用,就是Google为了提供给开发者一个在Java中调用C/C++代码的一个工作。NDK本身其实就是一个交叉工作链,包含了Android上的一些库文件,然后,NDK为了方便使用,提供了一些脚本,使得更容易的编译C/C++代码。总之,在Android的SDK之外,有一个工具就是NDK,用于进行C/C++的开发。一般情况,是用NDK工具把C/C++编译为.co文件,然后在Java中调用。
NDK不适用于大多数初学的Android工程师,对于许多类型的Android应用没有什么价值。因为它不可避免地会增加开发过程的复杂性,所以一般很少使用。那为什么Google还提供NDK,我们就一起研究下
2.为什么使用NDK
1、在平台之间移植其应用
2、重复使用现在库,或者提供其自己的库重复使用
3、在某些情况下提性能,特别是像游戏这种计算密集型应用
4、使用第三方库,现在许多第三方库都是由C/C++库编写的,比如Ffmpeg这样库。
5、不依赖于Dalvik Java虚拟机的设计
6、代码的保护。由于APK的Java层代码很容易被反编译,而C/C++库反编译难度大。
3.NDK到so
从上图这个Android系统框架来看,我们上层通过JNI来调用NDK层的,使用这个工具可以很方便的编写和调试JNI的代码。因为C语言的不跨平台,在Mac系统的下使用NDK编译在Linux下能执行的函数库——so文件。其本质就是一堆C、C++的头文件和实现文件打包成一个库。目前Android系统支持以下七种不用的CPU架构,每一种对应着各自的应用程序二进制接口ABI:(Application Binary Interface)定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库,对应关系如下
- ARMv5——armeabi
- ARMv7 ——armeabi-v7a
- ARMv8——arm64- v8a
- x86——x86
- MIPS ——mips
- MIPS64——mips64
- x86_64——x86_64
4.ndk环境搭建
第一种:下载好ndk,然后再as项目中配置
ndk下载地址: https://developer.android.google.cn/ndk/downloads/
选择适合自己需求的ndk,下载解压到指定目录,路径不要出现空格和中文。也可配置到全局路径path中方便全局使用命令
打开电脑属性
配置环境变量
注意:我这里是在用户变量里面配置的,当然你也可以在系统变量中配置
打开cmd输入ndk-build,配置成功
接下来就是给开发工具AS关联NDK
打开as在项目的local.properties配置文件中增加下面代码
ndk.dir=D\:\\android-ndk-r20
在gradle.properties中配置如下代码 用于兼容旧版本的NDK
android.useDeprecatedNdk=true
第二种:在AS中直接指定路径配置
在Studio中下载与配置NDK:Settings–Appearance & Behavior–System Settings–Android SDK–SDK Tools。
自此ndk环境配置完成,接下来就是编写ndk开发流程以及JNI结合,详见下文
三:JNI
1.什么是JNI
JNI(Java Native Interface,JNI)java本地开接口。相当于桥梁,是一种协议,通过jni可以达到java调用c/c++,c/c++调用java,即在 Java 虚拟机 (VM) 内部运行的 Java 代码能够与用其它编程语言(如 C、C++ 和汇编语言)编写的应用程序和库进行互操作。它提供了若干的API实现了Java和其他语言的通信(主要是C&C++)。通俗来说,就是JAVA调用C/C++函数的接口.如果你要想调用C系列的函数,你就必须遵守这样的约定.JNI 最重要的好处是它没有对底层 Java 虚拟机的实现施加任何限制。因此,Java 虚拟机厂商可以在不影响虚拟机其它部分的情况下添加对 JNI 的支持。程序员只需编写一种版本的本地应用程序或库,就能够与所有支持 JNI 的 Java 虚拟机协同工作。
提高效率:加密算法、图像处理、机器视觉等需要大量计算的逻辑,可以在C/C++中实现;
扩展JVM:扩展JVM的功能;
代码复用:复用C/C++平台上的代码,避免重复在Java上重复造轮子;
四:ndk+JNI开发
在NDK的开发过程中,有两种形式——ndk-build和CMake,其实两种方式最终的目的都是一样,将C 或 C++(“原生代码”)嵌入到 Android 应用。只是CMake使用起来更加方便,同时Android官方也推荐在Android Studio 2.2及以上使用CMake。工具的本质就是方便人们进行日常活动,对比一下两者流程,方便开发过程进行技术选型。
ndk-build方式进行ndk和jni开发
a. java目录下的java类中写native方法
public class JNI { static { System.loadLibrary("hello"); } /** * 在java类中定义一个native方法 * native 方法书写方式,类似于接口,但需要有关键字 native * @return */ public static native String sayHello(); }
b. src---> main 下创建 jni 文件夹,并在其下创建三个文件
c. 编写 .c或.cpp文件
#include <stdio.h> #include <stdlib.h> #include <jni.h> /*返回类型:返回java中对应的类型, *类名:Java_全路径_方法名 参数:JNIEnv *env 里面有很多方法,jobject obj表示谁调用这个方法就是谁的实例 当前是JNI.this */ //其中extern “C” 声明,是为了说明可能会用到 C 的代码 extern "C" { /* (1)函数名:JNIEXPORT + 返回类型 + JNICALL Java_+包名 + 类型 + 函数名(java中声明的),以下划线连接 (2)返回值类型,是 jni 中的数据类型,若没有返回类型,则使用 void (3)默认传入两个参数 JNIEnv* env(jvm运行环境), jobject obj(调用这个函数的Java对象) */ JNIEXPORT jstring JNICALL Java_com_javayihao_demo_JNI_sayHello(JNIEnv *env,jobject obj){ const char* text="hello jni"; return (*env)->NewStringUTF(env,text); } }
d. 配置编译环境
(1)Android.mk文件
.cpp 文件为 hello.cpp,对应的库的名字为 hello ,即生成的 so 为 hello .so
LOCAL_PATH := $(call my-dir) 指定cpp文件位置 include $(CLEAR_VARS) #编译时清除旧库 LOCAL_MODULE := lib LOCAL_PATH := $(call my-dir) 指定cpp文件位置 include $(CLEAR_VARS) #编译时清除旧库 LOCAL_MODULE := libhello #生成so的名字,前面加lib LOCAL_SRC_FILES := hello.cpp #需要编译的cpp文件 include $(BUILD_SHARED_LIBRARY) #注明生成动态库 #生成so的名字,前面加lib LOCAL_SRC_FILES := hello.cpp #需要编译的cpp文件 include $(BUILD_SHARED_LIBRARY) #注明生成动态库
(2)Application.mk文件
这个文件中一般进行ABI管理,告诉ndk-build生成适用于那些CPU指令集的库文件,=all就是编译生成所有CPU指令集的库文件
APP_ABI :=all
(3)build.gradle 配置
Android{ ... externalNativeBuild{ //指定Android.mk文件 ndkBuild{ path 'src/main/jni/Android.mk' } } //生成so到指定路径下 sourceSets{ main{ jni.srcDirs = [] jniLibs.srcDirs = ['libs'] } } }