Android(安卓)JNI开发入门之二
在上一篇文章《Android JNI开发入门之一》中,我介绍了Android应用程序(APK)怎样通过JNI调用Native C实现的共享库。本文将进一步介绍Android应用程序通过JNI调用Native C++实现的共享库,并实现一个和上文《Android JNI开发入门之一》相同功能的Helloworld应用程序。
两套不同的API
前文已经提到,Android系统的Java虚拟机为C和C++实现两套不同的API,所以我们调用的时候需要注意这一点儿。另外Google并没有提供 JNI的文档,我们调用的时候可以参考Android的jni.h文件,里面有C和C++的JNI函数原型。也可以把本例的相同功能HelloWorld 库和上文《Android JNI开发入门之一》进行比较。
C++实现HelloWorld共享库
在本例中Android应用程序不需要有任何变化,我们需要重新用C++实现HelloWorld共享库。创建com_simon_Helloworld.cpp文件,并在文件中输入如下内容:
- #include<jni.h>
- #defineLOG_TAG"HelloWorld"
- #include<utils/Log.h>
- /*
- *Class:com_simon_Helloworld
- *Method:print
- *Signature:()V
- */
- /*JNIEXPORTvoidJNICALLJava_com_simon_Helloworld_print(JNIEnv*,jobject)*/
- JNIEXPORTjstringJNICALLJava_com_simon_HelloWorld_printJNI(JNIEnv*env,jobjectobj)
- {
- LOGI("HelloWorldFromlibhelloworld.so!");
- returnenv->NewStringUTF("HelloWorld!");
- }
- staticconstchar*classPathName="com/simon/HelloWorld";
- staticJNINativeMethodmethods[]={
- {"printJNI","()Ljava/lang/String;",(void*)Java_com_simon_HelloWorld_printJNI},
- };
- /*
- *Registerseveralnativemethodsforoneclass.
- */
- staticintregisterNativeMethods(JNIEnv*env,constchar*className,
- JNINativeMethod*gMethods,intnumMethods)
- {
- jclassclazz;
- clazz=env->FindClass(className);
- if(clazz==NULL){
- LOGE("Nativeregistrationunabletofindclass'%s'",className);
- returnJNI_FALSE;
- }
- if(env->RegisterNatives(clazz,gMethods,numMethods)<0){
- LOGE("RegisterNativesfailedfor'%s'",className);
- returnJNI_FALSE;
- }
- returnJNI_TRUE;
- }
- /*
- *Registernativemethodsforallclassesweknowabout.
- *
- *returnsJNI_TRUEonsuccess.
- */
- staticintregisterNatives(JNIEnv*env)
- {
- if(!registerNativeMethods(env,classPathName,
- methods,sizeof(methods)/sizeof(methods[0]))){
- returnJNI_FALSE;
- }
- returnJNI_TRUE;
- }
- typedefunion{
- JNIEnv*env;
- void*venv;
- }UnionJNIEnvToVoid;
- /*Thisfunctionwillbecallwhenthelibraryfirstbeloaded*/
- jintJNI_OnLoad(JavaVM*vm,void*reserved)
- {
- UnionJNIEnvToVoiduenv;
- JNIEnv*env=NULL;
- LOGI("JNI_OnLoad!");
- if(vm->GetEnv((void**)&uenv.venv,JNI_VERSION_1_4)!=JNI_OK){
- LOGE("ERROR:GetEnvfailed");
- return-1;
- }
- env=uenv.env;;
- if(registerNatives(env)!=JNI_TRUE){
- LOGE("ERROR:registerNativesfailed");
- return-1;
- }
- returnJNI_VERSION_1_4;
- }
本例与上文《Android JNI开发入门之一》对比有如下几点不同需要注意:
1、C和C++实现共享库调用不同JNI API。前面已经提到Android系统JNI为C和C++提供了两套不同的API。请仔细对比NewStringUTF,GetEnv函数,就会发现JNI API不同。
2、C++版的helloworld共享库提供了函数映射表。前文《Android JNI开发入门之一》也已经提到,JNI API为了避免丑陋的函数名,提供了方法向Java虚拟机注册函数映射表。这样当Java调用Native接口的时候,Java虚拟机就可以不用根据函数名来决定调用哪个函数了,直接通过查询表格就可以找到需要调用的函数了。
3、我们注意到RegisterNatives第一个参数(C语言接口中是第二个参数)为调用该函数的Java类。这也和标准JNI函数名包含类名(包名和类名)的作用一样——声明那个Java类可以调用这个方法。
4、函数映射表的定义非常的怪异。你可以参考Android JNI 使用的数据结构JNINativeMethod详解和JNI标准手册相关类型的部分。
通过对比你会发现C++的实现同样功能的共享库比C加入更多的代码,另外你可能会有疑问既然Java虚拟机能用通过函数名访问到相应的Native code函数,为什么还要提供注册映射函数表呢?没错!作为一个HelloWorld程序,确实简单为第一要务!如果Java虚拟机能用函数名能访问到相 应的函数的话,我是不会多此一举来注册映射函数表。在实践中我发现:
标准JNI不能通过标准函数名找到C++实现的Helloworld共享库中的函数,但是C实现的helloworld共享没有这个问题。我不知道为什么会这样,请达人指教。没有办法才提供注册映射函数表。
下面提供一个helloworld共享库的Makefile——Android.mk:
- LOCAL_PATH:=$(callmy-dir)
- include$(CLEAR_VARS)
- LOCAL_SRC_FILES:=com_simon_Helloworld.cpp
- LOCAL_C_INCLUDES:=$(JNI_H_INCLUDE)
- LOCAL_MODULE:=libhelloworld
- LOCAL_SHARED_LIBRARIES:=libutils
- LOCAL_PRELINK_MODULE:=false
- include$(BUILD_SHARED_LIBRARY)
和前文《Android JNI开发入门之一》的Android.mk文件相比也就是修改了一下源文件,没有什么可说。编译生成libhelloworld.so文件,允许前文HelloWorld Android应用程序,你将会得到和前文相同的结果。
JNI的进一步学习
通过上面的例子我们已经初步掌握了Android编写JNI程序的方法。这也只能算是Android JNI入门而已,有很多JNI相关的内容我们在例子中并没有涉及到,比如:
1、我们并没有提到怎样在Native代码中回调Java的函数。
2、每个Native的函数中前两个参数是什么意思?
如果想用JNI进行Android应用开发我们需要更深入的学习。为了大家共同进步,我这里也可以提供一些相关的资料和方法。
如果我们想深入学习JNI首先要先熟悉标准的JNI。推荐大家学习《Java Native Interface: Programmer’s Guide and Specification》,权威的标准JNI学习文档。
前文提到Google没有Android JNI编程提供文档,但是在网上Simon也找到了一篇非常好的文档供大家参考——JNI Examples for Android。这篇文章中举了一个例子包含了Android JNI开发的方方面面,想深入学习Android JNI开发的朋友请仔细研读。
总结
俗话说得好“问道有先后,如是而已”,Simon也是刚刚才开始学习Android JNI编程,对JNI的理解上面难免有一些地方有错误,欢迎朋友们指正。
参考资料:
Android JNI 使用的数据结构JNINativeMethod详解
Android JNI实例
JNI Examples for Android
How to add a new module to Android
Android JNI(实现自己的JNI_OnLoad函数)
Android中JNI编程的那些事儿
Java Native Interface: Programmer’s Guide and Specification
Java Native Interface Specification
更多相关文章
- C语言函数的递归(上)
- Android(安卓)5.1 SurfaceFlinger VSYNC详解
- Android网络状态实时监听实例代码(二)
- linux kernel suspend Resume
- 关于Sytem.gc()主动触发Android(安卓)GC
- Android(安卓)客户端与服务器端进行数据交互(二、登录客户端)
- Android(安卓)Says Bonjour
- android编写Service入门用法与教程
- Android(安卓)suspend底层流程解析