android jni 学习之一 <资料>

 

写在前面的:

     一年前,在 android A20下面弄一个应用,当时要操作硬件串口uart,需要java与c进行交互,于是按照经典的流程: 用javah 生成一个头文件,然后我去实现头文件里面的函数。再后来,来了一哥们,弄了另外一种方法写Jni,搭建了一个框架,飘然而去,然后留下几个c的API让我去维护....半年过去了,最近,自己开始学习 c++部分的东西,于是开始来学习他的这部分代码。并在网上找了学习资料。

下面是我找到的资料和笔记。

 /**************************************************************************************************************/

先把自己找的资料贴在这里,感谢作者分享!

http://blog.csdn.net/jianguo_liao19840726/article/details/6719224

 简而言之:

1 android(java)代码中的本地方法列表(c/c++ 库提供的API),示例如下:

public class DataProvider {

    private static final class DataProviderHolder {

        private static final DataProvider instance = new DataProvider();

    }

    private DataProvider() {
    }

    public static DataProvider getInstance() {

        return DataProviderHolder.instance;

    }

/*本地方法实例一*/
    public synchronized native int Operator(String deviceMac, int deviceType,
            int state);

/*本地方法实例二*/
    public synchronized native int OperatorCmd(String deviceMac,
            int deviceType, int cmd, int state);

/*本地方法实例三*/
    public synchronized native String OperatorCmdString(String deviceMac,
            int deviceType, int cmd, int state);

/*本地方法实例四*/
    public native int OpenTty(String serialPort);

/*本地方法实例五*/
    public synchronized native String WriteDevice(String value,int type,int cmd,int val);
/*开始呼唤上面列出来的本地实例*/
static { System.loadLibrary("zigbee_r_lock"); } }

 2  在程序执行到  

System.loadLibrary("zigbee_r_lock");

这句代码的时候,会执行 loadLibrary()中的一个回调函数(callback),这个回调名字是:JNI_OnLoad().


3 c/c++的程序员来实现 JNI_OnLoad()
示例如下:
 1 jint JNI_OnLoad(JavaVM* vm, void* reserved)
 2 {
 3     JNIEnv* env = NULL;
 4     jint result = -1;
 5 
 6     device = new Device() ;
 7 
 8     if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
 9         return result;
10     }
11     assert(env != NULL);
12 
13     if (register_tuner_jni(env) < 0)
14     {
15         return result;
16     }
17 
18     result = JNI_VERSION_1_4;
19 
20     return result;
21 }

在 JNI_OnLoad()中,可以实现资源的分配,比如  new 一个对象;

注册本地方法;

指定jni 所使用的版本。

 

4  因为要注册的本地方法是各种各样的,于是自己实现一个注册方法即可:   register_tuner_jni(JNIEnv *env),代码如下:

static int register_tuner_jni(JNIEnv *env){
  return registerNativeMethods(env,"cn/acadiatech/telecom/box/engine/DataProvider", gMethods,  sizeof(gMethods) / sizeof(gMethods[0]));
}

registerNativeMethods(env,"cn/acadiatech/telecom/box/engine/DataProvider", gMethods,  sizeof(gMethods) / sizeof(gMethods[0]));

这句代码中的第二个参数,是一个类的名字。比如在eclipse某个xx.java文件中新建一个类后,它会生成一个pack或者类的路径。比如上面的DataProvider这个类就放在了eclipse的   src/cn/acadiatech/telecom/box/engine/DataProvider.java 文件中。于是,registerNativeMethods()的第二个参数<类名>  就写成了 "cn/acadiatech/telecom/box/engine/DataProvider"   如果这个类名搞错了,是跑不起来的。

看看代码吧:

 1 static int registerNativeMethods(JNIEnv* env, const char* className,
 2     JNINativeMethod* gMethods, int numMethods)
 3 {
 4     jclass clazz;
 5 
 6 
 7     clazz = env->FindClass(className);
 8     if (clazz == NULL) {
 9         return JNI_FALSE;
10     }
11     if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
12         return JNI_FALSE;
13     }
14 
15     return JNI_TRUE;
16 }

最关键的就是这句代码:

    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

其中的  gMethods  是一个函数指针列表 <数组>,如下:

 1 static JNINativeMethod  gMethods[] = {
 2     {"OperatorCmd","(Ljava/lang/String;III)I",(void *)Operator_device_Cmd},
 3     {"OpenTty","(Ljava/lang/String;)I",(void*)Open_tty_device},
 4     {"CloseTty","()V",(void*)Close_tty_device},
 5     {"SetTtySpeed","(I)I",(void*)Set_speed_tty_device},
 6     {"SetTtyParity","(I)I",(void*)Set_parity_tty_device},
 7     {"SetTtyBits","(I)I",(void*)Set_bits_tty_device},
 8     {"SetTtyStopBits","(I)I",(void*)Set_stop_bits},
 9     {"SetTtyFlowControl","(I)I",(void*)Set_flow_control_tty_device},
10     {"ListDevice","()Ljava/lang/String;",(void*)List_device},
11     {"ReadDevice","()Ljava/lang/String;",(void*)Read_device},
12     {"ListDevice_new","(Ljava/lang/String;III)Ljava/lang/String;",(void*)List_device_new},
13     {"WriteDevice","(Ljava/lang/String;III)Ljava/lang/String;",(void*)Write_device},
14     {"LedControl","(Ljava/lang/String;IIIII)Ljava/lang/String;",(void*)Led_Control},
15 //    {"OperatorCmdString","(Ljava/lang/String;III)Ljava/lang/String;",(void*)Operator_device_Cmd_String},
16 };

额... 对,数量是没有限制的,只是,自己需要将要注册函数的个数传输给   registerNativeMethods() 即可。

这个函数指针列表每一项分为三部分 :  

[1]  在java中的 Native名字--- java使用的时候,就使用这个名字

[2]  类型 ---- 包括参数类型 + 返回值类型

[3] c/c++ 中函数的名字,c/c++程序员要实现的,就是实现这个函数名字里面的东西

说说类型部分吧:

第一点: 

" () "   中的东西,表示了函数的参数类型,紧跟在括号后面的表示了返回值的类型。如果类型为 java 中的 String 类型,那么必须以下面这种格式写:

Ljava/lang/String;

注意它的格式: 以 L 开头,以  " ;"  分号结尾。

无论是返回值函数参数类型,都必须这种写法。

第二点: 

注意类型的转换:  比如自己要在c/c++中给 java层返回  char * / string 这种类型的数据,是不能直接返回的,需要经过类型转换才行的。此为后话,后面再提。

 

5  实现函数指针列表中的函数(c/c++部分的函数名字)

static jint
Open_tty_device(JNIEnv *env ,jobject obj ,jstring tty)
{
    const char* tty_name = env->GetStringUTFChars(tty,0) ;
    return device->open_tty_device(tty_name) ;
}
/*
 ** if success return this open device fd else error is -1 
 */
int Device::open_tty_device(const char* tty_name )
{    
    this->fd = ngb_Open((char*)tty_name) ;
    if (this->fd < 0) {
    }
    return this->fd ;
}

当然了,Device是一个类,里面包括了很多成员函数。于是,整个jni 的脉络框架就这样了。唯里面的细节部分需要注意了,比如资源的申请、类型的转换、具体的c++部分代码的实现了---这已经于jni 没关系了,而是c++的范畴了。

 

 

写在最后:  如果要调用硬件成功,android层需要添加一行  “加访问权限” 的代码,否则将无权限打开底层硬件---这仅仅是针对要限制权限的情况下。

 

笔记中所用到的代码位于github上:

https://github.com/boyisgood86/libzigbee

 

你可能感兴趣的:(android)