AndroidNDK技术-全面攻略

基本概念

JNI的定义

JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是c&c++)。从Javal.1开始, JNI标准成为Java平台的一部分,它允许Java代码和其他语言写的代码进行交互。

为什么要使用JNI?
效率上c/c++语言效率更高
代码移植,复用已经存在的c代码
java反编译比c语言容易

学习JNI必须要掌握的知识点
JAVA语言
c/c++ 语言
NDK(native development kits) 

夸平台概览

JNI的副作用
一旦使用JNI, JAVA程序就丧失了JAVA平台的两个优点
程序不再跨平台。要想跨平台,必须在不同的系统环境下重新编译本地语言部分
程序不再是绝对安全的,本地代码的不当使用可能导致整个程序崩溃。

Android中的五层架构
1.application:应用层
2.application framework:应用框架层
3.libraries和dalvik :函数库和虚拟机层
4.HAL(硬件抽象层)
5.linux kernel

JNI与NDK的关系

JNI与NDK的区别
JNI (Java native interface):在Google开发者指南中给出的定义如下: "JNI是Java和C++组件用以互相沟通的接口。
NDK(Native Development Kit):在Google开发者指南中给出的定义如下"Android NDK是一套允许您使用原生代码语言(例如C和C++)实现部分应用的工具集。

JN是一种技术,NDK是一个工具 

静态库与动态库 

什么是库?
库是写好的现有的,成熟的,可以复用的代码集合。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a. .lib)和动态库(.so. .dll) 。

二者的不同点在于代码被载入的时刻不同。
静态库的代码在编译过程中已经被载入可执行程序,因此体积较大。
共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。

动态库为什么又叫做共享库?
不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。

库文件的命名
静态库:libxxx.a
动态库:libxxxx.so.major.minor
*xxxx是该lib的名称, major是主版本号, minor是副版本号

查看可执行程序的依赖
linux下:
ldd命令可以查看一个可执行程序依赖的共享库。
例如#ldd/bin/Inlibc.so.6
=> /lib/libc.so.6 (0× 40021000)/lib/ld-linux.so.2 
=>/lib/d-linux.so.2 (0×40000000)
可以看到In命令依赖于libc库和ld-linux库。

mac下:
使用otool命令可以查看一个可执行程序依赖的共享库。
例如. #otool -L hello
=> /libmyhello1.so (compatibility version 0.0.0, current version 0.0.0)
=> /usr/lib/libSystem.B.dylib

 

hello.h

void hello(const char* str);

 hello.c

#include 
void hello(const char* name){ 
printf ("hello %s",name);
}

 main.c

#include"hello.h"
int main(){
hello ("san zhang");
return()
}

静态库生成使用

st hello.h                        //新建hello.h文件
st hello.c                        //新建hello.c文件
st main.c                         //新建main.c文件
gcc -c hello.c
ls
helio.c hello.h hello.o main. c
ar -cr libstatic.a hello.o
ls
libstatic. a main.chello.h hello.ohelio.c
gcc -o hello main.c -L.-lstatic
ls
hello
hello
hello world.i love this world% 

动态库生成使用

st hello.h                        //新建hello.h文件
st hello.c                        //新建hello.c文件
st main.c                         //新建main.c文件
gcc -c hello.c
ls
helio.c hello.h hello.o main. c
ar -cr libstatic.a hello.o
ls
libstatic. a main.chello.h hello.ohelio.c
gcc -o hello main.c -L.-lstatic
ls
hello
hello
hello world.i love this world% 

rm -rf hello hello.o libstatic.a   //删除静态库生成的文件
gcc -c hello.c
gcc -shared -o libdynamic.so hello.o  //生成静态库

gcc -o hellol main.c -L. -ldynamic     //生成可以执行文件hello1
hello1   //执行

otool -L hello1

 环境的搭建

NDK下载

下载途径
google官网下载链接:https://developer.android.google.cn/ndk/index.html

NDK目录的介绍
目录结构梳理
docs帮助文档
build/tools批处理文件
platforms存放jni开发所需要用到的一些头文件和动态链接库
prebuilt预编译用到的工具
sample使用jni的案例
ndk-build编译打包C代码的指令
toolchains不同平台的编译器链接器目录以及一些和编译连接相关的工具,gcc,ld等工具都在这个目录                                              sources NDK的部分源码

JNI Hello Word :https://blog.csdn.net/salute_li/article/details/52469214

2018 11 月自己编写hello word:https://mp.csdn.net/postedit/84073518

GitHub:https://github.com/smallletters/sample-code

日志的输出

如:#define LOGI(..)_ _ android_log_print(ANDROID_LOG_INFO,TAG,_VA_ARGS__)

编写native文件的另外一种方式--直接编写

 一般格式:
JNIEXPORT返回类型JNICALL Java_包名_类名-方法名(参数) {
}

JNI基础

字符串的操作

数组的访问

void (*GetIntArrayRegion)(JNIEnv*, jintArray,jsize a, jsize b,jint* buf)

将jintArray数组中从a-b中的数据复制到buf中

jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*):
将jintarray转换成jnitarray.如果isCopy不是NULL, *isCopy在复制完成后即被设为JNI_TRUE;如果未复制,则设为JNII_FALSE

jarray NewObjectArray(JNIEnv *env, jsize length, jclass elementClass, jobject initialElement):
构造新的数组,它将保存类elementClass中的对象。所有元素初始值均设为initialElement

 

对象字段的操作

. jclass GetObjectClass (JNIEnv *env, jobject obj):
通过对象获取这个类。该函数比较简单,唯一注意的是对象不能为NULL,否则获取的class肯定返回也为NULL。

. jfieldID GetFieldID (JNIEnv *env, jclass clazz, const char *name, const char *sig):
返回Java类(非静态)域的属性ID。

jobject GetObjectField(jobject obj,jfieldID fieldID):
根据fieldld获取相应的field

 void SetObjectField(jobject obj, jfieldID fieldID, jobject value):
设置对象成员变量的值

静态字段的操作

静态字段的访问
jfieldID GetStaticFieldID (JNIEnv *env,jclass clazz, const char*name, const char *sig):                                                                获取类的静态域ID方法 

静态字段的设置
void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value):
设置static field的值

访问静态字段与对象字段的比较
访问静态字段和对象实例字段的不同点:

访问静态字段使用GetStaticFieldID,而访问对象的实例字段使用GetFieldID,但是,这两个方法都有相同的返回值类型: jfieldID.

构造函数的调用

void GetCharArrayRegion (JNIEnv *env, ArrayType array, jsize start,jsize.• len, NativeType *buf):                                               将基本类型数组某一区域复制到缓冲区中的一组函数

void SetCharArrayRegion (JNIEnv *env, ArrayType array, jsize start, jsize.len, NativeType *buf):
将基本类型数组的某一区域从缓冲区中复制回来的一组函数

jobject NewObject (JNIEnv *env, jclass clazz, jmethodID methodD, ..):
构造新Java对象

使用时缓存

字段ID和方法ID可以在字段的值被访问或者方法被回调的时候缓存起来。
缓存字段ID
缓存方法ID

.C文件 缓存方法ID  C++代码

JNIEXPORT void JNICALL 包名—类名—方法名
(JNIEnv *env, jobject obj){
 jclass clsobj =(*env)-GetObjectclass (env, obj);
static jmethodID mid NULL;
if (mid == NULL) {
 mid = (*env)->GetMethodID(env, cLsobj, "sayHello", "()V");
}
(*env)->CallVoidMethod (env, obj, mid);

静态初始化的过程中缓存

我们在使用时缓存字段和方法的ID,每次本地方法被调用时都要检查ID是否已经被缓存。许多情况下,在字段ID和方法ID被使用前就初始化是很方便的。

异常的处理

jthrowable ExceptionOccurred (JNIEnv *env):
确定某个异常是否正被抛出。

void ExceptionDescribe (JNIEnv *env):
将异常及堆栈的回溯输出到标准输出

void ExceptionClear (JNIEnv *env):
清除当前抛出的任何异常。如果当前无异常,则不产生任何效果。

jint ThrowNew (JNIEnv *env, jclass clazz, const char *message):
利用指定类的消息(由message指定)构造异常对象并抛出该异常

JNI的性能测试

 

 

 



 

 

你可能感兴趣的:(Android,studio)