Android Jni使用案例

来源:互联网 发布:华为网络机顶盒密码 编辑:程序博客网 时间:2024/06/01 09:59

Android Jni使用案例

Android上层和C/C++层通信可以通过JNI实现,具体做法有两种:


一:采用默认的本地函数注册流程
  1). 编写带有native方法的Java类;
    Gpio.java 代码:
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">    package com.prowave.jnitest;  
  2.   
  3.   
  4.     public class Gpio {</span>  
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">            public String printJNI(String s) {  
  2.         return native_printJNI(s);  
  3.         }  
  4.         ...  
  5.         private native String nativePrintJNI(String inputstr);  
  6.         ...  
  7.     }</span>  
    
  2). 使用javah命令生成.h头文件;
    自动生成头文件com_prowave_jnitest_Gpio.h(头文件的命名格式是javah根据java类的包名类名自动生成):
[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">    #include <jni.h>  
  2.     /* Header for class com_prowave_jnitest_Gpio */  
  3.   
  4.     #ifndef _Included_com_prowave_jnitest_Gpio  
  5.     #define _Included_com_prowave_jnitest_Gpio  
  6.     #ifdef __cplusplus  
  7.     extern "C" {  
  8.     #endif  
  9.     /* 
  10.      * Class:     com_prowave_jnitest_Gpio 
  11.      * Method:    nativePrintJNI 
  12.      * Signature: (Ljava/lang/String;)Ljava/lang/String; 
  13.      */  
  14.     JNIEXPORT jstring JNICALL Java_com_prowave_jnitest_Gpio_nativePrintJNI  
  15.       (JNIEnv *, jclass, jstring);  
  16.   
  17.     #ifdef __cplusplus  
  18.     }  
  19.     #endif  
  20.     #endif</span>  
    
    方法名称Java_com_prowave_jnitest_Gpio_nativePrintJNI解释:Java_包名(层级关系用_分隔)_类名_方法名
    
  3). 编写Gpio.c代码实现头文件中的方法
    
[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">    #include <jni.h>  
  2.     #include <stdio.h>  
  3.     #include "com_prowave_jnitest_Gpio.h"  
  4.   
  5.     jstring JNICALL Java_com_prowave_jnitest_Gpio_nativePrintJNI(JNIEnv *env, jobject obj, jstring inputStr)  
  6.     {  
  7.         return (*env)->NewStringUTF(env, "Hello JNI! I am Native interface");  
  8.     }</span>  

  4). 编写Android.mk文件,通过NDK编译生成so文件(NDK集成开发环境需要自行安装)
    
[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">        LOCAL_PATH := $(call my-dir)  
  2.     include $(CLEAR_VARS)  
  3.   
  4.     LOCAL_MODULE    :Gpio  
  5.     LOCAL_SRC_FILES :Gpio.c  
  6.     include $(BUILD_SHARED_LIBRARY)</span>  

通过ndk-build编译最终生成libGpio.so文件,可供上层应用使用,比如调用Gpio.java的printJNI方法,最终获得C代码中Java_com_prowave_jnitest_Gpio_nativePrintJNI方法中返回的字符串。

注意事项:通过这种方式去实现JNI,Java代码中声明native方法时不能带下划线,比如nativePrintJNI方法不能写成native_printJNI,因为C/C++中的方法命名规则是“Java_包名(层级关系用_分隔)_类名_方法名”这种形式,以此来匹配Java类中的方法,下划线导致生成的头文件中方法名多出一个下划线,这样就匹配不上Java中定义的本地方法了。


生成头文件的方法有两个:

1). 用eclipse新建一个java项目或者android项目,在com/prowave/jnitest包下新建Gpio.java文件,添加上native方法,eclipse会自动编译java文件,生成Gpio.class文件在bin/class/com/prowave/jnitest/目录下。终端(window环境下可使用cygwin)进入bin同级目录,执行命令:javah -classpath bin/classes/ -d jni com.prowave.jnitest.Gpio,生成com_prowave_jnitest_Gpio.h头文件在jni目录。 

-classpath:指定路径,这里只要指定com/prowave/jnitest这个包所在的路径就可以了,并不是指Gpio.class的路径,所以classpath的参数为bin/classes/; 

-d jni:在当前路径生成一个jni文件夹;

com.prowave.jnitest.Gpio:java文件的包名类名。


2). 直接写一个xxx.java文件,通过javac xxx.java编译,生成xxx.class文件;新建文件目录,比如xxx.java文件里的包名是这样定义的:package com.prowave.jnitest;建立com/prowave/jnitest(这几个文件夹是层级关系),将xxx.class拷贝到最里面的文件夹jnitest;在当前目录打开终端,执行命令:javah -classpath . -d jni com.prowave.jnitest.Gpio,生成com_prowave_jnitest_Gpio.h。



二:自己重写JNI_OnLoad()函数
  1). 编写带有native方法的Java类;
    Gpio.java 代码:
[java] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">    package com.prowave.jnitest;  
  2.   
  3.     public class Gpio {  
  4.         ...  
  5.         private native String native_printJNI(String inputstr);  
  6.         private native String native_getChar();  
  7.         ...  
  8.     }</span>  
  2). 定义C/C++文件,重写JNI_OnLoad()函数 (Gpio.c文件)
       
[cpp] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">        #include <jni.h>  
  2.     #include <stdio.h>  
  3.     #include <stdlib.h>  
  4.     #include <string.h>  
  5.   
  6.         // 定义需要映射的Java类名称  
  7.     #define JNIREG_CLASS "com/prowave/jnitest/Gpio"  
  8.   
  9.     /* java类中定义的本地方法的具体实现 */  
  10.     jstring nativePrintJNI(JNIEnv *env, jobject obj, jstring inputStr)  
  11.     {  
  12.         const char *str = (const char *)(*env)->GetStringUTFChars( env,inputStr, JNI_FALSE );  
  13.         (*env)->ReleaseStringUTFChars(env, inputStr, (const char *)str );  
  14.         return (*env)->NewStringUTF(env, "Hello World! I am Native interface");  
  15.     }  
  16.   
  17.     /* java类中定义的本地方法的具体实现 */  
  18.     jstring printChar(JNIEnv *env, jobject obj)  
  19.     {  
  20.         return (*env)->NewStringUTF(env, "JNI Print Char Succ!");  
  21.     }  
  22.   
  23.     /* 本地方法和java类中定义的native方法映射关系 
  24.      * JNINativeMethod 
  25.      * const char* name;      java方法名称    
  26.      * const char* signature; java方法签名 
  27.      * void* fnPtr;           c/c++的函数指针  
  28.      */  
  29.     static JNINativeMethod methods[] = {  
  30.         { "native_printJNI""(Ljava/lang/String;)Ljava/lang/String;", (void*)nativePrintJNI },  
  31.         { "native_getChar""()Ljava/lang/String;", (void*)printChar },  
  32.     };  
  33.   
  34.     /*  
  35.      * Register several native methods for one class.  
  36.      */    
  37.     static int registerNativeMethods(JNIEnv* env, const char* className,  
  38.                                  JNINativeMethod* gMethods, int numMethods)  
  39.     {  
  40.         jclass clazz;  
  41.         clazz = (*env)->FindClass(env, className);  
  42.       
  43.         if (clazz == NULL) {  
  44.             return JNI_FALSE;  
  45.         }  
  46.         if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) != JNI_OK) {  
  47.             return JNI_FALSE;  
  48.         }  
  49.       
  50.         return JNI_TRUE;  
  51.     }  
  52.   
  53.     /*  
  54.      * Register native methods for all classes we know about.  
  55.      *  
  56.      * returns JNI_TRUE on success.  
  57.      */    
  58.     static int registerNatives(JNIEnv* env)  
  59.     {  
  60.         if (!registerNativeMethods(env, JNIREG_CLASS,  
  61.                                methods, sizeof(methods) / sizeof(methods[0]))) {  
  62.             return JNI_FALSE;  
  63.         }  
  64.         return JNI_TRUE;  
  65.     }  
  66.   
  67.     /* This function will be call when the library first be load. 
  68.     * You can do some init in the libray. return which version jni it support. 
  69.     */  
  70.     jint JNI_OnLoad(JavaVM* vm, void* reserved)  
  71.     {  
  72.         JNIEnv* env = NULL;  
  73.         if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) {  
  74.             return -1;  
  75.         }  
  76.       
  77.         if (!registerNatives(env)) {  
  78.             return -1;  
  79.         }  
  80.         return JNI_VERSION_1_6;  
  81.     }</span>  

  3). 编写Android.mk文件,通过NDK编译生成so文件(NDK集成开发工具需要安装)。
[html] view plain copy
 print?在CODE上查看代码片派生到我的代码片
  1. <span style="font-size:14px;">        LOCAL_PATH := $(call my-dir)  
  2.     include $(CLEAR_VARS)  
  3.   
  4.     LOCAL_MODULE    :Gpio  
  5.     LOCAL_SRC_FILES :Gpio.c  
  6.     include $(BUILD_SHARED_LIBRARY)</span>  

  通过这种方式注册的好处:
  1>. 函数名称可以自定义,无须遵循特殊的命名格式
  2>. 不需要通过javah生成头文件
  3>. 将本地函数向VM进行登记,VM能更有效率的去找到registerNativeMethods。

  4>. 可在执行期间进行抽换。由于gMethods[]是一个<名称,函数指针>对照表,在程序执行时,可多次呼叫registerNativeMethods()函数来更换本地函数之指针,而达到弹性抽换本地函数之目的。



附上案例源代码,下载地址:http://download.csdn.net/detail/visionliao/9532013


0 0
原创粉丝点击