Android架构师之路-JNI与NDK编程知识基础详解(c++音视频编码基础)

开发 前端
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。

[[412121]]

前沿小记

  • 1、Android架构师要学习的知识点有很多,后面我会总结下关于Android开发中jni和ndk开发的知识点,当前基础是越牢固越好,后期学习起来就不会太累,一点就懂;
  • 2、今天我们就来总结下jni和ndk的基础知识点;
  • 3、Android 平台从一开就已经支持了C/C++了。我们知道Android的SDK主要是基于Java的,所以导致了在用Android SDK进行开发的工程师们都必须使用Java语言。不过,Google从一开始就说明Android也支持JNI编程方式,也就是第三方应用完成可以通过JNI调用自己的C动态度;

一、什么是ndk

  • NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的;
  • NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so;
  • NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作;
  • NDK提供了一份稳定、功能有限的API头文件声明;Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog);
  • NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式;
  • 使用NDK,我们可以将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率;
  • 使用NDK,我们可以将需要保密的应用逻辑使用C开发。毕竟,Java包都是可以反编译的;
  • NDK促使专业so组件商的出现:比如视频库的编译、音频库、图片库滤镜等等;
  • NDK将使Android平台支持C开发的开端;NDK提供了的开发工具集合,使开发人员可以便捷地开发、发布C组件。同时,Google承诺在NDK后续版本中提高“可调式”能力,即提供远程的gdb工具,使我们可以便捷地调试C源码;

二、为什么使用NDK

在平台之间移植其应用;

重复使用现在库,或者提供其自己的库重复使用;

在某些情况下提性能,特别是像游戏这种计算密集型应用;

使用第三方库,现在许多第三方库都是由C/C++库编写的,比如Ffmpeg这样库;

不依赖于Dalvik Java虚拟机的设计;

代码的保护。由于APK的Java层代码很容易被反编译,而C/C++库反编译难度大;

三、jni详解

1、jni是什么?以及和ndk关系

  • JNI,全称为Java Native Interface,即Java本地接口,JNI是Java调用Native 语言的一种特性。通过JNI可以使得Java与C/C++机型交互。即可以在Java代码中调用C/C++等语言的代码或者在C/C++代码中调用Java代码。由于JNI是JVM规范的一部分,因此可以将我们写的JNI的程序在任何实现了JNI规范的Java虚拟机中运行;
  • 在Android Framework中,需要提供一种媒介或 桥梁,将Java层(上层)与C/C++层(下层)有机的联系起来,使得他们互相协调完成某些任务。而充当这种媒介的就是Java本地接口(JNI,Java Native Interface);
  • JNI提供一些列的接口,允许Java类与C/C++等本地编辑语言(在JNI中,这些语言被称为 本地语言)编写的应用 程序、模块 、库进行交互操作。比如,在Java类中使用C语言库中的函数或在C语言中使用 Java类库,都需要借助JNI;
  • Android NDK是一个开发工具集,提供一系列工具快速开发C/C++的动态库,并能自动将 .so/.dll 和 Java 应用一起打包到Apk;NDK提供工具可以方便JNI调用C/C++,而且提供了交叉编译器可以修改.mk文件生成特定CPU平台的动态库,并能将so和java应用一起打包到apk中;简单说就是JNI负责Java与C/C++进行互相操作,NDK提供工具方便在Android平台使用JNI;

2、JNI开发流程的步骤

图片
  • 在Java中先声明一个native方法;
  • 编译Java源文件javac得到.class文件;
  • 通过javah -jni命令导出JNI的.h头文件;
  • 使用Java需要交互的本地代码,实现在Java中声明的Native方法;
  • 将本地代码编译成动态库(Windows系统下是.dll文件,如果是Linux系统下是.so文件,如果是Mac系统下是.jnilib);
  • 通过Java命令执行Java程序,最终实现Java调用本地代码;
图片

3、 JNI数据结构

图片

JNI函数表的组成就像C++的虚函数表。虚拟机可以运行多张函数表,举例来说,一张调试函数表,另一张是调用函数表。JNI接口指针仅在当前线程中起作用。这意味着指针不能从一个线程进入另一个线程,然而,可以在不同的咸亨中调用本地方法;

  1. jdouble test (JNIEnv *env, jobject obj, jint i, jstring s) 
  2.      const char *str = (*env)->GetStringUTFChars(env, s, 0);  
  3.      (*env)->ReleaseStringUTFChars(env, s, str);  
  4.      return 10; 
图片

JNI有自己的原始数据类型和数据引用类型如下

图片

四、jni交互原理详解

1、JavaVM

JavaVM是Java虚拟机在JNI层的代表,JNI全局仅仅有一个JavaVM结构中封装了一些函数指针(或叫函数表结构),JavaVM中封装的这些函数指针主要是对JVM操作接口。另外,在C和C++中的JavaVM的定义有所不同,在C中JavaVM是JNIInvokeInterface_类型指针,而在C++中有对JNIInvokeInterface_进行了一次封装,比C中少了一个参数,这也是为什么JNI代码更推荐使用C++来编写的原因;

2、JNIEnv

JNIEnv是一个线程相关的结构体,该结构体代表了Java在本线程的执行环境

JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JVM中可能创建多个Java线程,每个线程对应一个JNIEnv结构,它们保存在线程本地存储TLS中。因此,不同的线程的JNIEnv是不同,也不能相互共享使用。JNIEnv结构也是一个函数表,在本地代码中通过JNIEnv的函数表来操作Java数据或者调用Java方法。也就是说,只要在本地代码中拿到了JNIEnv结构,就可以在本地代码中调用Java代码;

调用Java 函数:JNIEnv代表了Java执行环境,能够使用JNIEnv调用Java中的代码;

操作Java代码:Java对象传入JNI层就是jobject对象,需要使用JNIEnv来操作这个Java对象;

3、JNIEnv和JavaVM的区别

  • JavaVM:JavaVM是Java虚拟机在JNI层的代表,JNI全局仅仅有一个;
  • JNIEnv:JavaVM 在线程中的代码,每个线程都有一个,JNI可能有非常多个JNIEnv;

4、JNIEnv与线程

  • JNIEnv是线程相关的,即在每一个线程中都有一个JNIEnv指针,每个JNIEnv都是线程专有的,其他线程不能使用本线程中的JNIEnv,即线程A不能调用线程B的JNIEnv,所以JNIEnv不能跨线程;
  • JNIEnv只在当前线程有效:JNIEnv仅仅在当前线程有效;
  • JNIEnv不能在线程之间进行传递,在同一个线程中,多次调用JNI层方便,传入的JNIEnv是同样的;
  • 本地方法匹配多个JNIEnv:在Java层定义的本地方法,能够在不同的线程调用,因此能够接受不同的JNIEnv;

5、JNIEnv结构

图片

6、JNIEnv相关的常用函数

  1. 创建Java中的对象 
  2.     jobject     (*NewObject)(JNIEnv*, jclass, jmethodID, ...); 
  3.     jobject     (*NewObjectV)(JNIEnv*, jclass, jmethodID, va_list); 
  4.     jobject     (*NewObjectA)(JNIEnv*, jclass, jmethodID, const jvalue*); 
  5. 字符串相关 
  6.   jstring     (*NewString)(JNIEnv*, const jchar*, jsize); 
  7.     jsize       (*GetStringLength)(JNIEnv*, jstring); 
  8.     const jchar* (*GetStringChars)(JNIEnv*, jstring, jboolean*); 
  9.     void        (*ReleaseStringChars)(JNIEnv*, jstring, const jchar*); 
  10.     jstring     (*NewStringUTF)(JNIEnv*, const char*); 
  11.     jsize       (*GetStringUTFLength)(JNIEnv*, jstring); 
  12.     /* JNI spec says this returns const jbyte*, but that's inconsistent */ 
  13.     const char* (*GetStringUTFChars)(JNIEnv*, jstring, jboolean*); 
  14. 获取数组相关 
  15.  jsize GetArrayLength(jarray array) 
  16. return functions->GetArrayLength(this, array); } 
  17. jobject GetObjectArrayElement(JNIEnv *env, 
  18. jobjectArray array, jsize index); 

总结:

  • 以上是学习jni的基础知识点,必须理解的,不懂的就问就学;
  • jni还有很多知识点,比如动态注册,静态注册等等;
  • 要学习一点c语言和c++的基础知识点,后面会讲到总结。

本文转载自微信公众号「Android开发编程」,可以通过以下二维码关注。转载本文请联系Android开发编程公众号。

 

责任编辑:姜华 来源: Android开发编程
相关推荐

2021-07-26 05:26:54

Androidc++java

2023-03-03 15:40:43

抖音视频编码器

2011-07-13 18:24:18

C++

2013-04-19 15:12:17

架构师WEB架构师

2010-02-01 16:13:15

C++继承

2015-06-10 11:22:41

云计算云架构师

2010-01-19 14:45:35

C++语言

2010-01-19 10:16:40

C++程序

2010-02-02 09:43:27

C++存储区域

2011-07-15 01:38:56

C++this指针

2022-06-20 05:59:35

5G技术音视频技术安卓系统

2020-09-30 09:47:17

架构

2010-02-05 17:00:06

C++单例模式

2024-02-28 08:22:07

2011-07-14 17:24:10

C++面向对象思想

2011-07-14 17:37:02

C++面向对象编程思想

2009-08-24 18:22:05

C# 泛型编程

2011-11-17 16:26:49

AndroidAdobeAIR

2012-04-09 09:41:24

WEB架构师

2021-10-25 09:41:04

架构运维技术
点赞
收藏

51CTO技术栈公众号