在上一篇文章《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>#define LOG_TAG "HelloWorld"#include <utils/Log.h> /*  * Class:     com_simon_Helloworld  * Method:    print  * Signature: ()V  *//*JNIEXPORT void JNICALL Java_com_simon_Helloworld_print(JNIEnv *, jobject)*/JNIEXPORT jstring JNICALL Java_com_simon_HelloWorld_printJNI(JNIEnv *env, jobject obj){    LOGI("Hello World From libhelloworld.so!");return env->NewStringUTF("Hello World!");}static const char *classPathName = "com/simon/HelloWorld";static JNINativeMethod methods[] = {  {"printJNI", "()Ljava/lang/String;", (void*)Java_com_simon_HelloWorld_printJNI },};/* * Register several native methods for one class. */static int registerNativeMethods(JNIEnv* env, const char* className,    JNINativeMethod* gMethods, int numMethods){    jclass clazz;    clazz = env->FindClass(className);    if (clazz == NULL) {        LOGE("Native registration unable to find class '%s'", className);        return JNI_FALSE;    }    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        LOGE("RegisterNatives failed for '%s'", className);        return JNI_FALSE;    }    return JNI_TRUE;}/* * Register native methods for all classes we know about. * * returns JNI_TRUE on success. */static int registerNatives(JNIEnv* env){  if (!registerNativeMethods(env, classPathName,                 methods, sizeof(methods) / sizeof(methods[0]))) {    return JNI_FALSE;  }  return JNI_TRUE;}typedef union {    JNIEnv* env;    void* venv;} UnionJNIEnvToVoid;/* This function will be call when the library first be loaded */jint JNI_OnLoad(JavaVM* vm, void* reserved){    UnionJNIEnvToVoid uenv;    JNIEnv* env = NULL;    LOGI("JNI_OnLoad!");    if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {        LOGE("ERROR: GetEnv failed");        return -1;    }    env = uenv.env;;    if (registerNatives(env) != JNI_TRUE) {        LOGE("ERROR: registerNatives failed");        return -1;    }     return JNI_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:= $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES:=com_simon_Helloworld.cppLOCAL_C_INCLUDES := $(JNI_H_INCLUDE)LOCAL_MODULE := libhelloworldLOCAL_SHARED_LIBRARIES := libutilsLOCAL_PRELINK_MODULE := falseinclude $(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

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

使用javah -jni 命令之前,必须使用cmd先cd进入工程的classes文件夹F:\Eclipse\workspace\MyFirstJNI\bin\classes(切记,在操作过程中无需拷贝任何文件,在这个文件夹中包含了全路径如下F:\Eclipse\workspace\MyFirstJNI\bin\classes\com\cn\pcw,里面包含了文件MyFirstJNIActivity.class),那么命令就是javah -jni com.cn.pcw.MyFirstJNIAcrivity,成功后在classes文件夹下生成一个com_cn_pcw_MyFirstJNIActivity.h头文件

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

转贴:http://my.unix-center.net/~Simon_fu/?p=836

更多相关文章

  1. Android:你要的WebView与 JS 交互方式 都在这里了
  2. [Android] 利用java反射调用隐藏Api
  3. Android应用程序注冊广播接收器(registerReceiver)的过程分析
  4. 基于Eclipse的Android(安卓)JNI层测试应用开发过程记录
  5. Android7.0下调用相机闪退的解决方案
  6. Android(安卓)使用【AIDL】调用外部服务
  7. 用C/C++开发android应用
  8. Android(安卓)面试题(1)
  9. 箭头函数的基础使用

随机推荐

  1. centos7利用yum安装mysql 8.0.12
  2. mysql community server 8.0.12安装配置
  3. mysql 8.0.12 winx64详细安装教程
  4. mysql 8.0.12 解压版安装教程
  5. mysql 8.0.12 解压版安装教程 个人亲测!
  6. 详解MySQL双活同步复制四种解决方案
  7. mysql installer community 8.0.12.0安装
  8. mysql 8.0.12 安装配置方法图文教程(windo
  9. mysql 8.0.11 安装步骤详解
  10. mysql 8.0.12 简单安装教程