




已阅读5页,还剩23页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
搭建JNI的开发环境具体步骤: 双击打开。1、 安装:android-ndk下载地址在Android官网,如下图:下载后解压就可以用了,它里面的文档需要经常去查看使用:2、安装Cygwin安装时只安装这两个即可:devel 目录里面存放的是linux系统与编译相关的插件集合.shell 目录里面存放的是linux系统的脚本相关的插件集合.配置Linux的环境变量既然Cygwin就是一个Linux运行环境,那么一定也可以配置环境变量,因为ndk中的命令要在Linux下运行的话,首先要进入到ndk命令所在目录才能执行,这样很麻类,这时就可以配置Linux的环境变量了:找到:E:cygwinetc目录下的profile文件(没扩展名的),用记事本打开,找到第32行:PATH=/usr/local/bin:/usr/bin:$PATH ,首先要清楚在Linux下的目录结构是怎么样的,用cd . 一直返回到Linux的根目录, 然后输入ls命令,可以看到有一个sygdrive目录,用cd cygdrive 进入该目录,这时可以看到:c d e f g ,这些就是硬盘的盘符,所以在Windows系统下的E:android-ndk-r7b目录,在Linux中的表示为:/cygdrive/e/android-ndk-r7b,多个路径之间用 :号分隔,配好的PATH为:PATH=/cygdrive/e/android-ndk-r7b:/usr/local/bin:/usr/bin:$PATHCygwin中的命令使: 3、安装CDT:插件安装好后会多出一个C/C+的视频: 写一个ndkHelloWord的步骤:1. 创建一个android工程2. JAVA代码中写声明native 方法 3. 创建jni目录,编写c代码,方法名字要对应查看Java中对JNI的规范:jni.h文件 C语言的jobject是void*的别名C语言string是又jobject的别名在JNINativeInterface结构体里定义了很多的函数指针:通过函数指针就可以调用这些方法。怎么拿这个JNINativeInterface结果体呢?通过结构体指针就能拿到结构体: *JNIEnv而这里的 JNIEnv* 是一个结构体指针的指针,所以通过 *env拿到的就是JNIEnv,然后再*JNIEnv就拿到了JNINativeInterface结构体写成一个语句就是: *(*env) = JNINativeInterface结构体4编译.c文件生成动态库编译C文件需要在项目的根目录下执行编译命令编译.c文件用的是ndk中的ndk-build 命令,而这个命令需要运行在Linux的环境下,所以需要启动Cygwin程序,在Cygwin的命令窗口中进入到安卓项目的根目录,然后执行:/cygdrive/e/android-ndk-r7b/ndk-build 命令,如下图:这里出了一个错误,第一句:Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk 说指向了一个未知的文件所以需要把一个叫:Android.mk的文件创建出来放到项目中的jni文件夹中新建一个文件,输入文件名为:Android.mk这个Android.mk文件就是用来告诉编译器一些规,如要编译的是哪个C文件呀,编译出来的文件起个什么名字呀。5、创建Android.mk那这个Android.mk应该怎么写呢?可以查找帮助文档,如下图:把红色的拷到Android.mk文件中,然后指定交叉编译后的文件名与指定要编译哪个C文件。接着再到Cygwin程序中执行/cygdrive/e/android-ndk-r7b/ndk-build 命令,这时会报错这么一个错误:这是因为C文件中还有一些依赖的头文件还没导入,导入:#include,这时再运行就OK了,hello.c代码如下:#include#includejstring Java_com_itheima_ndkhelloword_DemoActivity_helloWorldFromC(JNIEnv* env, jobject obj)return (*(*env).NewStringUTF(env, hello from c);运行结果如下:编译成功后会在项目中生成这两些东西:调用Android.mk中起的名字hello,自动在前面加上lib,在后面加上.so6.Java代码load 动态库.调用native代码加载之后,在需要的地方调用: helloWorldFromC()方法即可查看生成的动态库进入项目中的bin目录,把apk文件解压,可以看到在:ndkHelloWorld_2libarmeabi 目录下有:libhello.so 这个动态库文件当把这个apk文件安装到手机手,这个动态库的位置在:data/data/ 目录下,可以看到这个动态库可以被其他应用程序读取和执行。修改动态库的权限为: 可读可写 可读 可读 -rw- r- r 二进制 -110 100 100换成十进制为:644,分析如图:用chmod 644 libhello.so就可以改变 libhello.so的权限,如果用chmod 000 libhello.so 则把它的权限改为全部不可读写不可执行。命令执行如图所示:带下划线的方法名注意事项在Java中声明的native方法名一般要不带下划线,如:public native String say_hell();那么在C文件中映射方法时为:Java_com_itheima_ndk4_DemoActivity_say_hello ,这是不对的,根据JNI的规范这是说在DemoActivity类的下面有个内部类say,在say中又有个内部类叫hello,解决办法:在方法的下划线后加入1,相当于转义了一样。如上面的代码改为:Java_com_itheima_ndk4_DemoActivity_say_1hello问题:如果有一个方法名为: hello_1_from() ,这又怎么写映射呀?这写起来会很麻烦,这时就可以使用一个JDK提供的工具:javah ,它可以帮我们生成jni的头文件,然后在C代码中导入头文件即可。在CMD命令行输入:javah 可以可看该命令的帮助信息:javah命令使用在D:test目录下建一个a.java文件,代码为:public class a public native String say_Hello();编译该文件生成.class文件,再执行 javah a ,这时就可以在D:test目录看到生成了一个“a.h”文件,用记事本打开:/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class a */#ifndef _Included_a#define _Included_a#ifdef _cplusplusextern C #endif/* * Class: a * Method: say_Hello * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_a_say_1Hello (JNIEnv *, jobject);#ifdef _cplusplus#endif#endif红色框中的即为自动生成的方法签名(映射)。如果Java的.class文件是带包名的,那么执行命令要带上包名:javah aa.bb.a所以我们想要生成安卓项目里的方法签名,就可以到classes目录下执行 javah命令,然后把生成的 .h 的文件拷贝到项目中的jni目录中,然后在C代码中用 “ #include “aa_bb_a.h” ”的方式把生成的头文件引入,这里用双引号,是说引入的是C源代码文件的当前目录的资源,如果要引用ndk中的这个目录下的头文件引入要用,如#include ,引用头文件之后,拷贝那个生成的native方法到C文件中,加以修改即可。Android.mk文件详解 #local_path 代表的是当前android.mk文件所在的路径 #$() 代表的是一个函数. LOCAL_PATH := $(call my-dir) #CLEAR_VARS 清空变量 # 清空所有以LOCAL_ 开头的变量里面的内容 (不会清空LOCAL_PATH里面的数据); include $(CLEAR_VARS) # 定义编译后的 c代码库 的名称. LOCAL_MODULE := Hello /这个名字我们可以显示的在名字前面加上: lib ,但不能在后面加 “ .so “,否则会报错。 #定义makefile 编译的源文件 #依赖的头文件 是不需要指定的. LOCAL_SRC_FILES := Hello.c #生成一个动态的代码库 include $(BUILD_SHARED_LIBRARY) # include $(BUILD_STATIC_LIBRARY); 生成一个静态的代码库 # 静态代码库的作用 主要就是用来提供一些库函数 编译的时候 可能需要用到静态代码库.Ndk中的库文件保存在:静态库 和 动态库的区别?一般来说 静态库扩展名 .a 静态库的体积比较大: 包含了所有的可执行的代码,使用的时候就不需要依赖其他代码库了 动态库扩展名 .so (Windows系统下的动态库为 .dll ) 动态库的体积很小: 包含了可执行代码的引用.在使用时,依赖于其他库,在用到哪个依赖库时才会去找那个引用,安卓手机中的库文件在:,动态库就是来这里找所依赖的库文件的。 Android.mk 的含义LOCAL_PATH:=$(call my-dir)LOCAL_PATH是定义源文件在哪个目录用的.my-dir 是个定义的宏方法, $(call my-dir)就是调用这个叫 my-dir的宏方法,这个方法返回值就是Android.mk文件所在的目录include $(CLEAR_VARS)CLEAR_BARS 变量是build system里面的一个变量这个变量指向了所有的类似 LOCAL_XXX的变量,执行完这一句话, 这个编译系统就把 所有的类似LOCAL_MODULE,_SRC_FILELOCALS,LOCAL_STATIC_LIBRARIES,.这样的变量都清除掉但是不会清除掉 LOCAL_PATHLOCAL_MODULE 就是你要生成的库的名字,名字要是唯一的这个.不能有空格.编译后系统会自动在前面加上lib的头, 比如说我们的Hello 就编译成了libHello.so还有个特点就是如果你起名叫libHello 编译后ndk就不会给你的module名字前加上lib了但是你最后调用的时候 还是调用Hello这个库LOCAL_SRC_FILES = :Hello.c这个是指定你要编译哪些文件不需要指定头文件 ,引用哪些依赖, 因为编译器会自动找到这些依赖 自动编译include $(BUILD_SHARED_LIBRARY) BUILD_STATIC_LIBRARY.so编译后生成的库的类型,如果是静态库.a 配置include $(BUILD_STATIC_LIBRARY)这是NDK r7版本之前的写法,而NDK r7版本以后就不能这样写了,r7版本后规定C+的扩展名为.cpp别的参数LOCAL_CPP_EXTENSION := cc /指定c+文件的扩展名,这个扩展名可以随便取LOCAL_MODULE := ndkfoo LOCAL_SRC_FILES := ndkfoo.ccLOCAL_LDLIBS += -llog -lvmsagent -lmpnet -lmpxml -lH264Android/指定需要加载一些别的什么库. ndk开发的常见错误1. 忘记书写android.mk 文件 an unknown file: ./jni/Android.mk$ ndk-buildAndroid NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk /cygdrive/c/android-ndk-r7b/build/core/add-application.mk:133: * Android NDK: Aborting. 。 停止。2. android.mk文件里面有非法字符$ ndk-buildjni/Android.mk:2: * 遗漏分隔符 。 停止。3. ndk-build命令 生成so文件的时候 有的时候 会报出来错误. 可以采用ndk-build clean 清空生成的中间文件.再调用ndk-build重新编译$ ndk-buildSharedLibrary : libHello.so./obj/local/armeabi/objs/Hello/Hello.o: file not recognized: 可以采用ndk-build clean 清空生成的中间文件.4.java.lang.UnsatisfiedLinkError: helloFromC一般就是库文件没有正常的加载到java虚拟机:System.loadLibrary(); 或者是方法的签名不正确 或者是库文件的名称不正确.java.lang.UnsatisfiedLinkError: Couldnt load Hell0: findLibrary returned null5.make: * obj/local/armeabi/objs/Hello/Hello.o Error 1c语言的语法出现了问题. 6.程序运行 突然异常终止, logcat控制台打印很多堆栈的寄存器地址. 代码的业务逻辑出了问题.#include #define LOG_TAG Daizhenliang#define LOGD(.) _android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, _VA_ARGS_)#define LOGI(.) _android_log_print(ANDROID_LOG_INFO, LOG_TAG, _VA_ARGS_)在c代码中使用logcat1、 C代码中增加 点这里复制代码#include引入的头文件都是在:android-ndk-r7bplatformsandroid-8arch-armusrinclude这个目录下的这里引入的 android/log.h 头文件在:由于这里使用到了log.h这个头文件,那么在编译的时候要把log.h对应的二进制代码库(liblog.so)引入到交叉编译的系统里面2、Android.mk文件增加LOCAL_LDLIBS += -llog /把一个函数库引入到交叉编译的系统里面这里引入了一个“liblog.so”函数库,引入的方式为 l 加 log(减掉前面lib,与后面的 .so) ,即 llog 就引入了 loblog.so这个函数库3、在方法体中使用LogCatLOGI(x=%d,x);LOGD(y=%d,y);也可以直接这样用:注:C语言中的LogCat不支持中文, 而且不能这样写:LOGD(“ab”,”ccc”);这样写只会输出 “ab”java 与 c之间的数据传递具体演示代码:ndkpassdata 项目演示代码在:ndkpassdata项目中l 在C语言中没有jstring的类型(也就是Java的String类型),可以通过个函数把jstring转的成C语言中字符数组:char* Jstring2CStr(JNIEnv* env, jstring jstr) char* rtn = NULL; jclass clsstring = (*env)-FindClass(env,java/lang/String); jstring strencode = (*env)-NewStringUTF(env,GB2312); jmethodID mid = (*env)-GetMethodID(env,clsstring, getBytes, (Ljava/lang/String;)B); jbyteArray barr= (jbyteArray)(*env)-CallObjectMethod(env,jstr,mid,strencode); / String .getByte(GB2312); jsize alen = (*env)-GetArrayLength(env,barr); jbyte* ba = (*env)-GetByteArrayElements(env,barr,JNI_FALSE); if(alen 0) rtn = (char*)malloc(alen+1); /0 memcpy(rtn,ba,alen); rtnalen=0; (*env)-ReleaseByteArrayElements(env,barr,ba,0); / return rtn;注:上面的语句中:char* rtn = NULL; 这里的NULL在C语言中表示数据为空的内存地址,要使用这个类型,需要导入#include l 可以通过 (*env)-NewStringUTF(env, hello_from_c ); 将一个C语言的字符数组变成一个jstring返回给Java是否生成数组的拷贝,0 为不生成Java中的int数组l 通过(*evn)-GetIntArrayElements(JNIEnv*,jintArray,jboolean*)方法把一个Java的int数组转的为C语言的int数组这个方法返回的是一个jint* 这是一个指针,也就是说是一个内存地址,这个地址就是这个jintArray数组的首地址。C语言中获取数组长度的函数:这个函数的返回值是:jsize,通过查找看到: 它是一个jint(Java中的int),它和C语言中的int其实是一样的,因为都是4个字节大小的。所以这个值可以直接赋值给C语言中的int所有的基本类型的数组都属于jarray:int* adrress = (*env)-GetIntArrayElements(env,arr,0);该方法返回的是数组的首地址,示例代码如下:JNIEXPORT jintArray JNICALL Java_com_itheima_logcat_provider_DataProvider_intMethod (JNIEnv * env, jobject obj, jintArray arr)int* adrress = (*env)-GetIntArrayElements(env,arr,0); /返回arr数组的首地址int len = (*env)-GetArrayLength(env, arr);int i;for(i=0;i 生成头文件 - c工程师实现头文件.2.历史存在的项目. c语言已经写好了. 包装这个c代码, 包装成一个适应于java代码调用的c代码.开发步骤:1、 了解C函数,在看C代码时,不需要去看代码体,只需要关心返回值、函数名、参数这三样2、 写安卓项目,根据C函数写native方法3、 javah 生成
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年公路水运工程试验检测师公共基础试题及答案(法规与技术标准)解析
- 全2025年公路水运试验检测人员考试题库及答案
- 2017年6月国开电大法律事务专科《行政法与行政诉讼法》期末纸质考试试题及答案
- 2025 年小升初沧州市初一新生分班考试英语试卷(带答案解析)-(人教版)
- 事业单位年度考核表个人总结2025教师7篇
- 北师大版灵宝市20252025学年度上期期末综合测试小学五年级语文试卷及参考答案
- 安徽省阜阳市界首市2024-2025学年八年级(下)期末物理试卷(含答案)
- 承包水立方合同范本
- 防疫车辆租车合同范本
- 工程劳务合同范本模板
- 消费券提振机制-洞察及研究
- 2026版创新设计高考总复习物理(人教基础版)学生用-学生内文答案
- 硅橡胶取模护理操作流程
- 电力营销稽查培训课件
- 老年人视力与听力能力评估方法
- 港口码头自然灾害应急措施
- 院前急救知识考核试题及答案
- 造价咨询合同管理办法
- 孤立性血管性眩晕
- 2026《衡中学案》高考一轮总复习 生物学 全书
- 问题性皮肤培训课件
评论
0/150
提交评论