Android NDK学习笔记3-入门案例篇
2016-09-22 17:49
423 查看
上篇文章我们安装了NDK系统,在NDK系统文件中包含samples文件夹,打开该文件夹,我们发现里面有大量的案例项目,这里我们通过Eclipse导入一个名为hello-jni的项目
导入成功后,我们可以看到项目目录如下:
然后,我们逐个学习案例源码
这里我给大家做个注释
下面我对代码中的各个函数做一下讲解
C代码中,JNIEnv指向JNINativeInterface结构的指针,为了访问任何一个JNI函数,该指针需要首先被解引用。调用格式如下:
C++代码中,JNIEnv实际上是C++类实例,JNI函数以成员函数的形式存在。
在标准的Java平台下,每个Process里可以产生很多JavaVM对象,每个JavaVM对象都有一个与之对应的JavaVM对象,但是在Android平台上,每个Process只能产生一个DalvikVM对象,也就是说在一个Android的进程中是通过有且只有一个虚拟器对象来服务所有Java和C++代码的。
1、JNIEnv 内部包含一个Pointer,Pointer指向Dalvik的JavaVM对象的Fanction Table,JNIEnv 关于程序执行环境的众多函数正是来源于Dalvik虚拟机
2、Android中每当一个Java线程第一次要调用本地C/C++代码时,Dalvik虚拟机实例会为该Java线程产生一个JNIEnv *指针
3、Java每条线程在和C/C++互相调用时,JNIEnv*是相互独立的,互不干扰
4、每本地的C/C++代码想获得当前线程所要使用的JNIEnv时,可以使用Dalvik VM对象的Java VM* jvm->getEnv()方法,该方法即会返回当前线程所在的JNIEnv*
a.原生实例方法定义:
因为静态方法没有与实例绑定,因此通过第二个参数获取类引用而不是实例引用,第二个参数是jclass值类型的。
b.原生静态方法定义:
● 默认情况下,NDK-Build脚本应该在主项目目录中执行,-C参数可以用于指定命令行中NDK项目的位置,这样一来NDK-Build脚本可以从任意位置开始:
● 如果源文件没被修改,Android NDK构建系统不会重构建目标。可以用-B执行NDK-Build脚本来强制重构建所有源代码。
● 为了清理生成的二进制文件和目标文件,可以在命令行执行ndk-build clean命令。Android NDK构建系统会删除生成的二进制文件。
● Android NDK构建系统依赖与GUN Make工具对模块进行构建。默认情况下GUN Make工具一次执行依据构建命令,等这一句执行完了以后再执行下一句。如果在命令行使用-j参数,GUN Make就可以并行执行构建命令。另外也可以通过执行该参数之后的数字来指定并行执行的命令总数。
OK,我们开始编译项目,在cmd命令行下,我们首先进入HelloJni项目的目录中,然后执行ndk-build命令。
编译成功后,我们发现整个项目目录生成libs和obj两个文件夹
这里给大家解释一下这些文件的用途
jni:该目录包含原生组件的源代码及描述原生组件构建方法的Android.mk构建文件。Android NDK构建系统将该目录作为NDK项目目录并希望在项目根目录中找到它。mk文件不用改动,是留给ndk-build编译使用的。
Libs:在Android NDK构建系统的构建过程中创建该目录,它包含指定的目标机体系结构的独立子目录,例如ARM的armeabi,在打包过程中,该目录被包含在APK文件中。
Obj:这是一个中间目录,编译源代码后产生的目标文件都被保存在该目录下,开发人员不需要访问该目录。
最后,我们运行项目,得到我们想要的结果
导入成功后,我们可以看到项目目录如下:
然后,我们逐个学习案例源码
1.声明Native方法
打开com.example.hellojni目录下的HelloJni.javapackage com.example.hellojni; import android.app.Activity; import android.widget.TextView; import android.os.Bundle; public class HelloJni extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* Create a TextView and set its content. * the text is retrieved by calling a native * function. */ TextView tv = new TextView(this); tv.setText( stringFromJNI() ); setContentView(tv); } /* A native method that is implemented by the * 'hello-jni' native library, which is packaged * with this application. */ public native String stringFromJNI(); /* This is another native method declaration that is *not* * implemented by 'hello-jni'. This is simply to show that * you can declare as many native methods in your Java code * as you want, their implementation is searched in the * currently loaded native libraries only the first time * you call them. * * Trying to call this function will result in a * java.lang.UnsatisfiedLinkError exception ! */ public native String unimplementedStringFromJNI(); /* this is used to load the 'hello-jni' library on application * startup. The library has already been unpacked into * /data/data/com.example.hellojni/lib/libhello-jni.so at * installation time by the package manager. */ static { System.loadLibrary("hello-jni"); } }
这里我给大家做个注释
2.实现原生方法
打开jni目录下的hello-jni.c#include <string.h> #include <jni.h> /** * 原生方法stringFromJNI也用一个名为Java_com_example_hellojni_HelloJni_stringFromJNI的完全 * 限定的函数来声明,这种显示的函数命名让虚拟机在加载的共享库中自动查找原生函数。函数命名规则为: *Java_类全路径_方法名。如Java_com_example_hellojni_HelloJni_stringFromJNI,其中Java_是函数的前 *缀,com_example_hellojni_HelloJni是类名,stringFromJNI是方法名,它们之间用 _(下划线) 连接 */ jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )// 第一个参数JNIEnv是指向可用JNI函数表的接口指针 // 第二个参数jobject是HelloJni类实例的Java对象引用 { // env指向java当前线程的实例,访问一个NewStringUTF函数 return (*env)->NewStringUTF(env, "Hello Castiel" ABI "."); }
下面我对代码中的各个函数做一下讲解
(1)JNIEnv接口指针
原生代码通过JNIEnv接口指针提供的各种函数来使用虚拟机的功能。JNIEnv是一个执行线程-局部数据的指针,而线程-局部数据中包含执行函数表的指针。C代码中,JNIEnv指向JNINativeInterface结构的指针,为了访问任何一个JNI函数,该指针需要首先被解引用。调用格式如下:
return (*env)->NewStringUTF(env,"hello china");
C++代码中,JNIEnv实际上是C++类实例,JNI函数以成员函数的形式存在。
return env->NewStringUTF("hello china");
在标准的Java平台下,每个Process里可以产生很多JavaVM对象,每个JavaVM对象都有一个与之对应的JavaVM对象,但是在Android平台上,每个Process只能产生一个DalvikVM对象,也就是说在一个Android的进程中是通过有且只有一个虚拟器对象来服务所有Java和C++代码的。
1、JNIEnv 内部包含一个Pointer,Pointer指向Dalvik的JavaVM对象的Fanction Table,JNIEnv 关于程序执行环境的众多函数正是来源于Dalvik虚拟机
2、Android中每当一个Java线程第一次要调用本地C/C++代码时,Dalvik虚拟机实例会为该Java线程产生一个JNIEnv *指针
3、Java每条线程在和C/C++互相调用时,JNIEnv*是相互独立的,互不干扰
4、每本地的C/C++代码想获得当前线程所要使用的JNIEnv时,可以使用Dalvik VM对象的Java VM* jvm->getEnv()方法,该方法即会返回当前线程所在的JNIEnv*
(2)实例方法与静态方法
Java程序中有两类方法:实例方法和静态方法。静态方法和实例方法均可以声明为原生的,可以通过JNI技术以原生代码的形式提供它们的实现。原生实例方法通过第二个参数获取实例引用,该参数是jobject类型的。如:a.原生实例方法定义:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )
因为静态方法没有与实例绑定,因此通过第二个参数获取类引用而不是实例引用,第二个参数是jclass值类型的。
b.原生静态方法定义:
JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jclass clazz )
JNI提供了自己的数据类型从而让原生代码了解java数据类型
3.使用NDK-Build脚本编译
开发过程中我们可以使用NDK-Build脚本启动Android NDK构建系统。● 默认情况下,NDK-Build脚本应该在主项目目录中执行,-C参数可以用于指定命令行中NDK项目的位置,这样一来NDK-Build脚本可以从任意位置开始:
ndk-build -C /path/to/the/project
● 如果源文件没被修改,Android NDK构建系统不会重构建目标。可以用-B执行NDK-Build脚本来强制重构建所有源代码。
ndk-build -B
● 为了清理生成的二进制文件和目标文件,可以在命令行执行ndk-build clean命令。Android NDK构建系统会删除生成的二进制文件。
ndk-build clean
● Android NDK构建系统依赖与GUN Make工具对模块进行构建。默认情况下GUN Make工具一次执行依据构建命令,等这一句执行完了以后再执行下一句。如果在命令行使用-j参数,GUN Make就可以并行执行构建命令。另外也可以通过执行该参数之后的数字来指定并行执行的命令总数。
ndk-build -j 4
OK,我们开始编译项目,在cmd命令行下,我们首先进入HelloJni项目的目录中,然后执行ndk-build命令。
编译成功后,我们发现整个项目目录生成libs和obj两个文件夹
这里给大家解释一下这些文件的用途
jni:该目录包含原生组件的源代码及描述原生组件构建方法的Android.mk构建文件。Android NDK构建系统将该目录作为NDK项目目录并希望在项目根目录中找到它。mk文件不用改动,是留给ndk-build编译使用的。
Libs:在Android NDK构建系统的构建过程中创建该目录,它包含指定的目标机体系结构的独立子目录,例如ARM的armeabi,在打包过程中,该目录被包含在APK文件中。
Obj:这是一个中间目录,编译源代码后产生的目标文件都被保存在该目录下,开发人员不需要访问该目录。
最后,我们运行项目,得到我们想要的结果
相关文章推荐
- Android NDK学习笔记3-入门案例篇
- Android NDK学习笔记3-入门案例篇
- Android自定义View入门及实战案例分析
- Android简易实战教程--第二十七话《自定义View入门案例之开关按钮详细分析》
- WebService入门介绍及案例分析
- struts的快速入门案例(用户登录验证)——手动配置方式
- 《mysql使用hibernate入门案例》
- css笔记16:盒子模型的入门案例
- AngularJS入门案例
- SSM入门(案例)
- CentOS 6.6下JDK1.7安装与配置(Linux)经典入门详解案例
- Spring学习(1):控制反转(IoC)和依赖注入(DI)的详解以及注解(annotation)开发入门案例
- 机器学习入门系列02,Regression 回归:案例研究
- 【02】框架学习—Hibernate第一个入门案例详解
- 安卓入门——拨打电话和发送短信案例
- 爬虫系列2:scrapy项目入门案例分析
- Swift应用案例 2.闭包入门到精通
- Hibernate入门配置案例
- 闪魂--Flash CS4完美入门与案例精解
- SpringMVC入门案例