常见概念
交叉编译
在一个平台上编译出来另外一个平台可以运行的二进制代码.不同的操作系统(windows, mac os,linux),不同的处理器平台(x86,arm,MIPS)
交叉编译的原理
源代码--->编译---->链接---->可执行性程序
模拟另外一种平台的cpu和操作系统的特性.
交叉编译的工具链
一大堆工具,放在一起,链式调用,工具链
常见工具
- ndk : native develop kits 本地开发工具集(交叉编译工具链)
- cdt : c/c++ develop tools c和c++开发工具 (eclipse的一个插件) 语法的高亮显示
- Cygwin : windows下一个linux模拟器(了解)
NDK的目录结构
- docs : 开发文档
- build: linux下编译的批处理命令
- platform : 某种平台下编译需要的头文件和函数库
- prebuild : 预编译的工具
- sample: 实例代码
- sources : 一些工具链的源码
- toolschains: 工具链
- ndk-build.cmd: ndk编译的命令脚本
JNI开发的步骤
-
在java代码里面声明一个native的方法.
public native String helloFromC();
-
在工程目录下面创建一个jni的文件夹.
-
在jni文件夹里面编写c代码
//env是虚拟机的环境 obj调用者对象 jstring Java_包名_类名_方法名(JNIEnv* env,jobject obj){ }
-
实现c代码
使用env环境指向的结构体,有自带的函数,利用jvm的函数进行操作 return (*(*env)).NewStringUTF(env,arr); return (*env)->NewStringUTF(env,arr);
-
编译c代码
配置c代码编译的脚本文件 Android.mk文件 LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello //编译后的模块名 LOCAL_SRC_FILES := Hello.c //编译的源文件的名称 include $(BUILD_SHARED_LIBRARY)
-
调用ndk-build.cmd指令编译c代码
注意配置环境变量
-
生成一个.so的文件 ( c代码编译出来的二进制可执行文件)
-
在java代码里面写静态代码块,加载so文件
static{ System.loadLibrary("hello"); }
-
像使用一般java方法一样调用native的方法.
jni开发的常见错误
10-22 02:18:35.672: E/AndroidRuntime(1552): Caused by: java.lang.UnsatisfiedLinkError: Couldn't load hello: findLibrary returned null* 如果处理器平台不匹配,返回的lib就是空
在Application.mk文件中编写APP_ABI := all
- 检查lib的名字是否拼写错误
10-22 02:24:50.418: E/AndroidRuntime(1696): Caused by: java.lang.UnsatisfiedLinkError: Native method not found: com.itheima.hello2.MainActivity.add:(II)I
- 检查c语言里面编写的方法名是否符合规范 Java包名类名_方法名(参数)
掌握要点
- 把基本类型的数据传递给c语言.
- String 字符串 传递给c语言(工具方法 jstring2cstr)
- 传递java数组给c语言
c代码回调java
- 复用已经存在的java代码
- c语言需要给java一些通知
- c代码不方便实现的逻辑
回顾一下反射
//1.把字节码装载进来
Class clazz = Demo.class.getClassLoader().loadClass("Dialog");
//2.查询对话框里面的方法
Method method = clazz.getDeclaredMethod("showDialog", String.class);
//3.调用方法
method.invoke(clazz.newInstance(), "哈哈嘎嘎嘎");
实际开发的流程
- java程序员先去定义native的方法.
- jni程序员根据native的方法生成函数的签名
- 找个c工程师 给我添代码.
- 配置编译环境,编译调用
jni使用过程和代码集锦
jni使用过程
使用eclipse中的ndk插件
- 1.修改工程的默认编码为utf-8...工程目录右键property编码
- 2.导入ndktool...工程目录右键androidTool下的clear lint makers
- 3.导入c源码库...工程目录右键proper下的cgeneral下的pathandsymbols点击add....filesystem..选择ndk的目录下的platform下的一个include文件应用
- 4.在main中新建一个native方法...此方法需要在c语言中实现.即是java中调用本地native方法.native方法在c中实现
- 5.main中添加静态代码块..加载时就加载c库文件
- 6.找到工程目录中的bin目录下的com.执行命令javah 包名类名.生成h文件查看文件中生成的cnative方法..复制进去jni下的c语言文件中..加上括号就是方法
- 7.实现方法即可
- 注意..没有在eclipse工具集的环境下,每一步文件夹都需要自己创建和添加...其中需要手动编译c文件....在.c文件的目录下执行指令ndk-build.cmd...需要配置环境变量..并且需要配置android.mk文件...加上application文件可以在四个处理器上跑
上述是java中调用c代码过程
- c代码也可以调用java代码
- 1.在main方法中定义好方法a();
- 2.在c代码中利用反射原理..可以调用到c代码///
- 三行代码调用java方法...详细如下
-
下面的参数字节码的获得.可以在class所在的目录执行命令javap -s 包名类名则可以找到
//jclass (*FindClass)(JNIEnv*, const char*); //1.查找字节码...第二个参数是方法所在的包名加上类名 jclass jclazz = (*env)->FindClass(env,"com/itheima/alipay/MainActivity"); //2.查找方法.......第二个参数所要调用的方法名...第三个参数是方法参数的字节码 // jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*); jmethodID methodid = (*env)->GetMethodID(env,jclazz,"showDialog","(Ljava/lang/String;)V"); //3.调用方法.第二个之后的即为参数.newstringutf方法返回以utf-8形式返回的java字符串 //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...); (*env)->CallVoidMethod(env,obj,methodid,(*env)->NewStringUTF(env,cstr));