本篇文章用于记录Android开发学习过程中的一些理解和遇到的一些问题解决,以防忘记,好记性不如烂博客,O(∩_∩)O。
本篇相对于前面介绍的led操作增加了串口、485和can的应用。对于led的驱动、hal、app我这里就不再介绍,可以参考前面的文章Android应用开发 led 驱动层 hal硬件抽象层 应用层 详细教程记录(含源码)。
一般来说,Linux系统下对串口、485和can的操作都是基于节点的操作,如tty***、can*这样的节点,所以对于应用程序来说,只要有这些设备节点即可完成操作了,所以本篇文章介绍的串口和485应用可以兼容任何一款Android设备或者开发板,can不一定,因为can在系统服务中做了固定设置,默认使用的是can0,一般来系统中只有一个can节点,且为can0,当然不排除其他can1的情况,读者在使用时这个要注意。由于485跟串口区别不是很大,所以直接可以按照串口协议来进行操作,使用的也是tty***这样的节点设备,当然如果要像使用串口一样来使用485,那么在硬件上是要做特别设计的,可以参考下图所示的电路。
下面就主要介绍hal和app这两层,驱动层是由设备节点完成,只要系统中的设备节点没有问题,那么通信就是正常的,测试节点可在Linux系统下使用nfs进行调试测试(nfs个人认为有助于加快调试速度,因为无需下载等繁琐的操作)。
对于hal层我这里就直接贴代码,不再一步一步做出解释,代码后边会做一些必要的说明,主要是有疑问,需要注意的地方。
代码如下:
#include <stdio.h> #include <stdlib.h>#include <termios.h>#include <unistd.h> #include <sys/types.h>#include <sys/stat.h> #include <fcntl.h>#include <string.h>#include "jni.h"#include "JNIHelp.h"#include <assert.h> #include "can.h"#include <sys/socket.h>#include <net/if.h>#include <cutils/properties.h>#include <sys/wait.h>// 引入log头文件#include <android/log.h> // log标签#define TAG "Led_Load_JNI"// 定义info信息#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)// 定义debug信息#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)// 定义error信息#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)#define DEVICE_NAME "/dev/real_led"//#define NO_REGISTER/*LED JNI*/#ifndef NO_REGISTERstatic jint Java_realarm_hardware_HardwareControl_LedSetState (JNIEnv *env, jobject thiz,jint ledNum,jint ledState)#elseJNIEXPORT jint JNICALL Java_realarm_hardware_HardwareControl_LedSetState (JNIEnv *env, jobject thiz,jint ledNum,jint ledState)#endif{ int fd = open(DEVICE_NAME, 0); if (fd == -1) { LOGE("led open error"); return 1; } if(ledState == 0) LOGD("Led close success"); else if(ledState == 1) LOGD("Led open success"); else { LOGD("Led ledState parameters ERROR.Only 0 or 1."); return 1; } ledState &= 0x01; ioctl(fd, ledState, 0); close(fd); return 0;}/*LED JNI*//*UART JNI*/int serialfd=-1;static speed_t getBaudrate(jint baudrate){ switch(baudrate) { case 0: return B0; case 50: return B50; case 75: return B75; case 110: return B110; case 134: return B134; case 150: return B150; case 200: return B200; case 300: return B300; case 600: return B600; case 1200: return B1200; case 1800: return B1800; case 2400: return B2400; case 4800: return B4800; case 9600: return B9600; case 19200: return B19200; case 38400: return B38400; case 57600: return B57600; case 115200: return B115200; case 230400: return B230400; case 460800: return B460800; case 500000: return B500000; case 576000: return B576000; case 921600: return B921600; case 1000000: return B1000000; case 1152000: return B1152000; case 1500000: return B1500000; case 2000000: return B2000000; case 2500000: return B2500000; case 3000000: return B3000000; case 3500000: return B3500000; case 4000000: return B4000000; default: return -1; }}/* * Class: cedric_serial_SerialPort * Method: open * Signature: (Ljava/lang/String;)V */static jobject Java_realarm_hardware_HardwareControl_OpenSerialPort (JNIEnv *env, jobject thiz, jstring path, jint baudrate, jint flags){ int fd; speed_t speed; jobject mFileDescriptor; LOGD("baudrate %d", baudrate); /* Check arguments */ { speed = getBaudrate(baudrate); if (speed == -1) { /* TODO: throw an exception */ LOGE("Invalid baudrate"); return NULL; } } /* Opening device */ { jboolean iscopy; const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscopy); LOGD("Opening serial port %s", path_utf); fd = open(path_utf, O_RDWR); serialfd=fd; LOGD("open() fd = %d", fd); (*env)->ReleaseStringUTFChars(env, path, path_utf); if (fd == -1) { /* Throw an exception */ LOGE("Cannot open port"); /* TODO: throw an exception */ return NULL; } } /* Configure device */ { struct termios cfg; LOGD("Configuring serial port"); if (tcgetattr(fd, &cfg)) { LOGE("tcgetattr() failed"); close(fd); /* TODO: throw an exception */ return NULL; } cfmakeraw(&cfg); cfsetispeed(&cfg, speed); cfsetospeed(&cfg, speed); if (tcsetattr(fd, TCSANOW, &cfg)) { LOGE("tcsetattr() failed"); close(fd); /* TODO: throw an exception */ return NULL; } } // Create a corresponding file descriptor { jclass cFileDescriptor = (*env)->FindClass(env, "java/io/FileDescriptor"); jmethodID iFileDescriptor = (*env)->GetMethodID(env, cFileDescriptor, "<init>", "()V"); jfieldID descriptorID = (*env)->GetFieldID(env, cFileDescriptor, "descriptor", "I"); mFileDescriptor = (*env)->NewObject(env, cFileDescriptor, iFileDescriptor); (*env)->SetIntField(env, mFileDescriptor, descriptorID, (jint)fd); } return mFileDescriptor;}/* * Class: cedric_serial_SerialPort * Method: close * Signature: ()V */static void Java_realarm_hardware_HardwareControl_CloseSerialPort (JNIEnv *env, jobject thiz){/* jclass SerialPortClass = (*env)->GetObjectClass(env, thiz); jclass FileDescriptorClass = (*env)->FindClass(env, "java/io/FileDescriptor"); jfieldID mFdID = (*env)->GetFieldID(env, SerialPortClass, "mFd", "Ljava/io/FileDescriptor;"); jfieldID descriptorID = (*env)->GetFieldID(env, FileDescriptorClass, "descriptor", "I"); jobject mFd = (*env)->GetObjectField(env, thiz, mFdID); jint descriptor = (*env)->GetIntField(env, mFd, descriptorID); ALOGD("close(fd = %d)", descriptor); close(descriptor);*/ LOGE("close(fd = %d)", serialfd); if(-1!=serialfd) close(serialfd);}/*UART JNI*//*RS485 JNI*//*realarm开发板对485的使能做了硬件处理,所以485操作与串口并无区别*//*RS485 JNI*//*CAN JNI*/int canfd=-1;struct sockaddr_can addr;void my_strcpy(char *dest, char *src, size_t n){ char i = 0; while(i < n) { *(dest++) = *(src++); i++; }}int my_system(const char * cmd) { FILE * fp; int res; char buf[1024]; if (cmd == NULL) { LOGD("my_system cmd is NULL!\n"); return -1; } if ((fp = popen(cmd, "r") ) == NULL) { LOGE("popen"); LOGE("popen error: %s/n", strerror(errno)); return -1; } else { while(fgets(buf, sizeof(buf), fp)) { LOGD("%s", buf); } if ( (res = pclose(fp)) == -1) { LOGE("close popen file pointer fp error!\n"); return WEXITSTATUS(res); } else if (res == 0) { return WEXITSTATUS(res); } else { LOGD("popen res is :%d\n", res); return WEXITSTATUS(res); } } LOGI("popen success!\n"); return -1;} static void Java_realarm_hardware_HardwareControl_InitCan (JNIEnv *env, jobject thiz, jint baudrate){ /* Check arguments */ switch (baudrate) { case 5000 : case 10000 : case 20000 : case 50000 : case 100000 : case 125000 : LOGI("Can Bus Speed is %d",baudrate); break; default: LOGI("Can Bus Speed is %d.if it do not work,try 5000~125000",baudrate); } /* Configure device */ if(baudrate!=0) { char str_baudrate[16]; sprintf(str_baudrate,"%d", baudrate); property_set("net.can.baudrate", str_baudrate); LOGI("str_baudrate is:%s", str_baudrate); property_set("net.can.change", "yes"); /*//下面的方法无法实现波特率的设置,命令未正确执行 char cmd[100]; sprintf(cmd, "%s%s", "su ip link set can0 up type can bitrate ", str_baudrate); LOGI("cmd is:%s", cmd); my_system("busybox ifconfig can0 down"); my_system(cmd); my_system("busybox ls"); */ } }static jint Java_realarm_hardware_HardwareControl_OpenCan (JNIEnv *env, jobject thiz){ struct ifreq ifr; int ret; /* Opening device */ canfd = socket(PF_CAN,SOCK_RAW,CAN_RAW); if(canfd==-1) { LOGE("Can Write Without Open"); return 0; } strcpy((char *)(ifr.ifr_name),"can0"); ioctl(canfd,SIOCGIFINDEX,&ifr); addr.can_family = AF_CAN; addr.can_ifindex = ifr.ifr_ifindex; bind(canfd,(struct sockaddr*)&addr,sizeof(addr)); return canfd;}static jint Java_realarm_hardware_HardwareControl_CanWrite (JNIEnv *env, jobject thiz, jint canId, jstring data){ int nbytes; int num = 0, i = 0; struct can_frame frame; jboolean iscopy; const char *send_data = (*env)->GetStringUTFChars(env, data, &iscopy); frame.can_id = canId; if(strlen(send_data) > 8)//用于支持当输入的字符大于8时的情况,分次数发送 { num = strlen(send_data) / 8; for(i = 0;i < num;i++) { my_strcpy((char *)frame.data, &send_data[8 * i], 8); frame.can_dlc = 8; sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr)); } memset((char *)frame.data, 0, 8); my_strcpy((char *)frame.data, &send_data[8 * i], strlen(send_data) - num * 8); frame.can_dlc = strlen(send_data) - num * 8; sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr)); nbytes = strlen(send_data); } else { my_strcpy((char *)frame.data, send_data, strlen(send_data)); frame.can_dlc = strlen(send_data); sendto(canfd,&frame,sizeof(struct can_frame),0,(struct sockaddr*)&addr,sizeof(addr)); nbytes = strlen(send_data); } (*env)->ReleaseStringUTFChars(env, data, send_data); LOGD("write nbytes=%d",nbytes); return nbytes;}static jobject Java_realarm_hardware_HardwareControl_CanRead (JNIEnv *env, jobject thiz, jobject obj, jint time){ unsigned long nbytes,len; struct can_frame frame = {0}; int k=0; jstring jstr; char temp[16]; fd_set rfds; int retval; struct timeval tv; tv.tv_sec = time; tv.tv_usec = 0; bzero(temp,16); if(canfd==-1){ LOGE("Can Read Without Open"); frame.can_id=0; frame.can_dlc=0; }else{ FD_ZERO(&rfds); FD_SET(canfd, &rfds); retval = select(canfd+1 , &rfds, NULL, NULL, &tv); if (retval == -1){ LOGE("Can Read slect error"); frame.can_dlc=0; frame.can_id=0; }else if (retval){ nbytes = recvfrom(canfd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr,&len); for(k = 0;k < frame.can_dlc;k++) temp[k] = frame.data[k]; temp[k] = 0; frame.can_id = frame.can_id - 0x80000000;//读得的id比实际的有个80000000差值,这里需要处理一下 LOGD("Can Read slect success."); }else{ frame.can_dlc=0; frame.can_id=0; //LOGD("Can no data."); } } jclass objectClass = (*env)->FindClass(env,"realarm/hardware/CanFrame"); jfieldID id = (*env)->GetFieldID(env,objectClass,"can_id","I"); jfieldID leng = (*env)->GetFieldID(env,objectClass,"can_dlc","C"); jfieldID str = (*env)->GetFieldID(env,objectClass,"data","Ljava/lang/String;"); if(frame.can_dlc) { LOGD("can_id is :%d", frame.can_id); LOGD("can read nbytes=%d", frame.can_dlc); LOGD("can data is:%s", temp); } (*env)->SetCharField(env, obj, leng, frame.can_dlc); (*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,temp)); (*env)->SetIntField(env, obj, id, frame.can_id); return obj;}static void Java_realarm_hardware_HardwareControl_CloseCan (JNIEnv *env, jobject thiz){ if(canfd!=-1) close(canfd); canfd=-1; LOGD("close can0");}/*CAN JNI*/#ifndef NO_REGISTERstatic JNINativeMethod gMethods[] = { {"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, {"OpenSerialPort", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void *)Java_realarm_hardware_HardwareControl_OpenSerialPort}, {"CloseSerialPort", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseSerialPort}, {"InitCan", "(I)V", (void *)Java_realarm_hardware_HardwareControl_InitCan}, {"OpenCan", "()I", (void *)Java_realarm_hardware_HardwareControl_OpenCan}, {"CanWrite", "(ILjava/lang/String;)I", (void *)Java_realarm_hardware_HardwareControl_CanWrite}, {"CanRead", "(Lrealarm/hardware/CanFrame;I)Lrealarm/hardware/CanFrame;", (void *)Java_realarm_hardware_HardwareControl_CanRead}, {"CloseCan", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseCan}, }; static int register_android_realarm_test(JNIEnv *env) { jclass clazz; static const char* const kClassName = "realarm/hardware/HardwareControl"; /* look up the class */ clazz = (*env)->FindClass(env, kClassName); //clazz = env->FindClass(env,kClassBoa); if (clazz == NULL) { LOGE("Can't find class %s\n", kClassName); return -1; } /* register all the methods */ if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) //if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) { LOGE("Failed registering methods for %s\n", kClassName); return -1; } /* fill out the rest of the ID cache */ return 0;}#endifjint JNI_OnLoad(JavaVM* vm, void* reserved) { #ifndef NO_REGISTER JNIEnv *env = NULL; if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) { //if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { LOGI("Error GetEnv\n"); return -1; } assert(env != NULL); if (register_android_realarm_test(env) < 0) { printf("register_android_realarm_test error.\n"); LOGE("register_android_realarm_test error."); return -1; }#endif /* success -- return valid version number */ LOGI("/*****************realarm**********************/"); return JNI_VERSION_1_4;}led需要注意的地方没什么可说的,串口部分我会在can中做说明。
下面是第一个需要说明的知识点:
// 引入log头文件#include <android/log.h> // log标签#define TAG "Led_Load_JNI"// 定义info信息#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)// 定义debug信息#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)// 定义error信息#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)可以看到这部分是一些宏定义,定义了LOGI、LOGD、LOGE这三个宏。目的是能够在Android调试的时候可以通过logcat查看到调试信息,对应的Android层的api分别是Log.i、Log.d、Log.e,所以是很必要的。
可以看到要使用这个功能需要__android_log_print这个函数,当然也需要#include <android/log.h>这个头文件。__android_log_print这个函数有三个参数,这里拿LOGI来说明,可以看到LOGI的实际代码是__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__),第一个参数ANDROID_LOG_INFO是告诉Android系统当前是属于信息类log,第二个参数TAG是指示是哪一个部分的信息(这里是#define TAG "Led_Load_JNI",这样易区分是哪里打印的信息,也有助于调试),第三个参数__VA_ARGS__就是LOGI输入的要打印的信息了。具体的解释,大家谷歌吧。
当然了,如果不想这么定义直接使用__android_log_print函数也是可以的,不过看起应该很烦吧。
下面是第二个解释的函数:
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span><pre name="code" class="cpp">static void Java_realarm_hardware_HardwareControl_InitCan (JNIEnv *env, jobject thiz, jint baudrate){ /* Check arguments */ switch (baudrate) { case 5000 : case 10000 : case 20000 : case 50000 : case 100000 : case 125000 : LOGI("Can Bus Speed is %d",baudrate); break; default: LOGI("Can Bus Speed is %d.if it do not work,try 5000~125000",baudrate); } /* Configure device */ if(baudrate!=0) { char str_baudrate[16]; <span style="color:#ff0000;">sprintf(str_baudrate,"%d", baudrate); property_set("net.can.baudrate", str_baudrate); LOGI("str_baudrate is:%s", str_baudrate); property_set("net.can.change", "yes");</span> <span style="color:#3333ff;">/*//下面的方法无法实现波特率的设置,命令未正确执行 char cmd[100]; sprintf(cmd, "%s%s", "su ip link set can0 up type can bitrate ", str_baudrate); LOGI("cmd is:%s", cmd); my_system("ifconfig can0 down"); my_system(cmd); my_system("busybox ls"); */</span> } }
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">首先来看上面函数的红色部分,sprintf和LOGI函数我这里就不说了,自行谷歌或百度。这里重要说明的是property_set这个函数,该函数是用于访问设置系统的环境属性,该函数是用于c/c++中的,在java中有其他的函数来代替,具体的可以参考http://blog.sina.com.cn/s/blog_55465b470101ngpv.html、http://blog.csdn.net/jackyu613/article/details/6136620等文章,百度或谷歌有很多。</span>现在关键的是使用这个环境变量设置怎么就能够能够完成can的波特率设置呢。说起还是比较曲折呢,思路是这样的,在Android系统的时候设置一个can波特率设置的服务,并设置一个启动该服务的一个条件,当启动条件变成yes的时候,can波特率设置的服务就会重启一次,进而完成设置。
具体实现方法下面介绍。
/device/nexell/realarm目录下的init.realarm.rc最后的代码如下:
##############################**CAN**#################################### chmod 0777 /system/bin/can.sh service can /system/bin/can.sh oneshoton property:net.can.change=yes restart can#########################################################################可以看到添加了一个can服务,以及net.can.change这个系统属性,那么启动can服务就是通过net.can.change这个系统属性是否为yes来触发的。
前面的红色代码最后一句可以看到就是设置net.can.change为yes属性。
那么net.can.baudrate这个属性怎么用的呢,可以看到红色代码中设置了这个属性,它是存储波特率的。它的使用时在启动的服务can.sh中,代码如下(位置在/device/nexell/realarm/can):
#!/system/bin/shsetprop net.can.change nonew_baudrate=`getprop net.can.baudrate`ifconfig can0 downip link set can0 up type can bitrate $new_baudrate从can.sh代码中可以知道,先设置net.can.change为no以关闭触发,然后通过getprop(Android系统提供getprop和setprop来读取和设置系统属性的值)命令来获取net.can.baudrate属性的值(在上面红色代码中所设置的值),然后通过ip link set can0 up type can bitrate $new_baudrate命令来完成can的设置和启动。
这样can的设置就可以完成了。
另外前面代码还有蓝色部分,该部分被注释掉了,这个是我在尝试另一种设置波特率的方法,只是未成功,不过这里我也说一下原理,若有朋友找到了原因,可以告知我,感激不敬。
蓝色代码部分主要想通过使用my_system这个函数来执行shell命令完成can的设置,理论上是可以的,可是实际上却不行,因为无论怎么样都无法正确执行ifconfig can0 down和ip命令,但是ls等其他指令却可以正常执行,搞不懂了。如果ifconfig can0 down和ip能够正常执行,那么代码就更简单了。
下面是my_system()函数的实现:
int my_system(const char * cmd) { FILE * fp; int res; char buf[1024]; if (cmd == NULL) { LOGD("my_system cmd is NULL!\n"); return -1; } if ((fp = popen(cmd, "r") ) == NULL) { LOGE("popen"); LOGE("popen error: %s/n", strerror(errno)); return -1; } else { while(fgets(buf, sizeof(buf), fp)) { LOGD("%s", buf); } if ( (res = pclose(fp)) == -1) { LOGE("close popen file pointer fp error!\n"); return WEXITSTATUS(res); } else if (res == 0) { return WEXITSTATUS(res); } else { LOGD("popen res is :%d\n", res); return WEXITSTATUS(res); } } LOGI("popen success!\n"); return -1;}关于popen和pclose的使用,以及c提供的system()函数,大家百度谷歌吧。
下面是第三个要解释的部分:
#ifndef NO_REGISTERstatic JNINativeMethod gMethods[] = { {"LedSetState", "(II)I", (void *)Java_realarm_hardware_HardwareControl_LedSetState}, {"OpenSerialPort", "(Ljava/lang/String;II)Ljava/io/FileDescriptor;", (void *)Java_realarm_hardware_HardwareControl_OpenSerialPort}, {"CloseSerialPort", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseSerialPort}, {"InitCan", "(I)V", (void *)Java_realarm_hardware_HardwareControl_InitCan}, {"OpenCan", "()I", (void *)Java_realarm_hardware_HardwareControl_OpenCan}, {"CanWrite", "(ILjava/lang/String;)I", (void *)Java_realarm_hardware_HardwareControl_CanWrite}, <span style="color:#ff0000;">{"CanRead", "(Lrealarm/hardware/CanFrame;I)Lrealarm/hardware/CanFrame;", (void *)Java_realarm_hardware_HardwareControl_CanRead},</span> {"CloseCan", "()V", (void *)Java_realarm_hardware_HardwareControl_CloseCan}, }; static int register_android_realarm_test(JNIEnv *env) { jclass clazz; static const char* const kClassName = "realarm/hardware/HardwareControl"; /* look up the class */ clazz = (*env)->FindClass(env, kClassName); //clazz = env->FindClass(env,kClassBoa); if (clazz == NULL) { LOGE("Can't find class %s\n", kClassName); return -1; } /* register all the methods */ if ((*env)->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) //if (env->RegisterNatives(env,clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK) { LOGE("Failed registering methods for %s\n", kClassName); return -1; } /* fill out the rest of the ID cache */ return 0;}#endifjint JNI_OnLoad(JavaVM* vm, void* reserved) { #ifndef NO_REGISTER JNIEnv *env = NULL; if ((*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_4) != JNI_OK) { //if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { LOGI("Error GetEnv\n"); return -1; } assert(env != NULL); if (register_android_realarm_test(env) < 0) { printf("register_android_realarm_test error.\n"); LOGE("register_android_realarm_test error."); return -1; }#endif /* success -- return valid version number */ LOGI("/*****************realarm**********************/"); return JNI_VERSION_1_4;}这部分是关于jni向上层提供api注册的部分。关于注册可以参考文章http://blog.csdn.net/wang_shuai_ww/article/details/44456755,关于JNINativeMethod这个结构体,百度谷歌更好。我这里只解释上面代码中红色部分。
一般的类型有如下:
字符
V
Z
I
J
D
F
B
C
S
数组则以"["开始,用两个字符表示
[I
[F
[B
[C
[S
[D
[J jlongArray
[Z
Ljava/net/Socket;
我们需要的结构是CanFrame结构,这个在Android系统中并未提供,而是我们应用的java代码中自定义的一个类,所在的包是realarm.hardware,类名是CanFrame,所以红色代码的写法就是Lrealarm/hardware/CanFrame,指定包和类名。从下图可以看到我们定义的类:
所以对于这类自定义的类型,在native注册时,参数(JNINativeMethod第二个参数)说明的方法是L+包名+类名。这个一定要理解,否则对于写jni有很大障碍,不懂的多谷歌JNINativeMethod这个结构的说明。
下面是第四个要解释的部分:
从第三部分的CanRead函数可知,Java_realarm_hardware_HardwareControl_CanRead函数要接收一个参数jobject obj,是jobject 类型的,实际上是CanFrame类型的,从第三个解释的部分可以知道。CanFrame和jni这层定义的can_frame怎么联系的呢。
先贴出代码,下面慢慢说。
static jobject Java_realarm_hardware_HardwareControl_CanRead (JNIEnv *env, jobject thiz, jobject obj, jint time){ unsigned long nbytes,len; struct can_frame frame = {0}; int k=0; jstring jstr; char temp[16]; fd_set rfds; int retval; struct timeval tv; tv.tv_sec = time; tv.tv_usec = 0; bzero(temp,16); if(canfd==-1){ LOGE("Can Read Without Open"); frame.can_id=0; frame.can_dlc=0; }else{ FD_ZERO(&rfds); FD_SET(canfd, &rfds); retval = select(canfd+1 , &rfds, NULL, NULL, &tv); if (retval == -1){ LOGE("Can Read slect error"); frame.can_dlc=0; frame.can_id=0; }else if (retval){ nbytes = recvfrom(canfd, &frame, sizeof(struct can_frame), 0, (struct sockaddr *)&addr,&len); for(k = 0;k < frame.can_dlc;k++) temp[k] = frame.data[k]; temp[k] = 0; frame.can_id = frame.can_id - 0x80000000;//读得的id比实际的有个80000000差值,这里需要处理一下 LOGD("Can Read slect success."); }else{ frame.can_dlc=0; frame.can_id=0; //LOGD("Can no data."); } } <span style="color:#ff0000;"> jclass objectClass = (*env)->FindClass(env,"realarm/hardware/CanFrame"); jfieldID id = (*env)->GetFieldID(env,objectClass,"can_id","I"); jfieldID leng = (*env)->GetFieldID(env,objectClass,"can_dlc","C"); jfieldID str = (*env)->GetFieldID(env,objectClass,"data","Ljava/lang/String;");</span> if(frame.can_dlc) { LOGD("can_id is :%d", frame.can_id); LOGD("can read nbytes=%d", frame.can_dlc); LOGD("can data is:%s", temp); } <span style="color:#ff0000;"> </span><span style="color:#3333ff;">(*env)->SetCharField(env, obj, leng, frame.can_dlc); (*env)->SetObjectField(env, obj, str, (*env)->NewStringUTF(env,temp)); (*env)->SetIntField(env, obj, id, frame.can_id);</span> return obj;}在上面代码中最不好理解的可能就是红色部分了,而这部分就是关键的部分。
(*env)->FindClass:获得CanFrame结构,并保存于objectClass
(*env)->GetFieldID:或者objectClass类中的成员,也就是CanFrame结构里的成员。该函数前两个参数不解释了容易理解,第三个参数和第四个参数比较重要,第三个参数标示CanFrame结构里的成员名称(需要名字一致,否则会出错的),第四个是该成员的类型。
下面举个例子
(*env)->GetFieldID(env,objectClass,"can_id","I")意思是获取objectClass类中的成员can_id,且类型为int型。与下图中CanFrame的定义对比:
can_id为Int型,是对应的。其他两个也同理,都是对应的。获得了java的类型后,下面看看是怎么联系的。
数据交换就是上面蓝色部分的代码了。
(*env)->SetCharField(env, obj, leng, frame.can_dlc);第二个参数就是Java_realarm_hardware_HardwareControl_CanRead接收的jobject obj参数,leng就是指can_dlc的fieldID,frame.can_dlc就是can读取到的字节数数据了。通过这样一句话,就把frame.can_dlc数据放到了obj中的can_dlc变量中了。
其他两个函数都类似,只是数据类型不同而已。最后把obj返回给java层,那么一个完整测CanFrame帧就读取完成了。
到此本篇介绍的JNI层,以及需要注意的地方就介绍完了,下一篇是app应用。
源码下载地址:http://download.csdn.net/detail/u010406724/8539263
转载请注明出处:http://blog.csdn.net/wang_shuai_ww/article/details/44672531