第一步, 在eclipose里新建一个android工程,再新建一个 SerialPort.java文件
package android.serialport; public class SerialPort { //本类方法,这里略过,主要是调用jni方法以给其他类提供方法。 ...... // JNI 方法 private native static FileDescriptor open(String path, int baudrate) throws IOException; private native void native_close(); private native int native_read_array(byte[] buffer, int length) throws IOException; private native int native_read_direct(ByteBuffer buffer, int length) throws IOException; private native int native_read_array_timeval(byte[] buffer, int length, int tv_sec, int tv_usec) throws IOException; private native int native_read_direct_timeval(ByteBuffer buffer, int length, int tv_sec, int tv_usec) throws IOException; private native void native_write_array(byte[] buffer, int length) throws IOException; private native void native_write_direct(ByteBuffer buffer, int length) throws IOException; static { System.loadLibrary("serial_port"); } }
第二步,生成共享库头文件。进入eclipose刚才所新建工程的/bin/classes/目录下。可以看到其目录下的android/serialport/SerialPort.class文件,这个文件就是eclipose自动编译生成的classes文件。
执行: javah android.serialport.SerialPort (注意把目录符“/”换成“.”,而且没有.classes)
就会在当前目录下生成android_serialport_SerialPort.h 头文件,查看android_serialport_SerialPort.h有时会多余些错误处,如Java_android_serialport_SerialPort_native_1open函数名,明显多了一个“1”,此处不懂为什么多了个“1”。但千万不要去掉它.
第三步,将刚才生成的H文件拷贝到jni源文件目录。因为我们这里是用ubuntu下ndk编译的。所以将android_serialport_SerialPort.h改名成SerialPort.h后,实现SerialPort.c文件,SerialPort.c就是要实现刚才SerialPort.h里面的所有方法。
SerialPort.h : 用javah 命令产生的
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class android_serialport_SerialPort */ #ifndef _Included_android_serialport_SerialPort #define _Included_android_serialport_SerialPort #ifdef __cplusplus extern "C" { #endif /* * Class: android_serialport_SerialPort * Method: native_open * Signature: (Ljava/lang/String;I)Ljava/io/FileDescriptor; */ JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_native_1open (JNIEnv *, jclass, jstring, jint); /* * Class: android_serialport_SerialPort * Method: native_close * Signature: ()V */ JNIEXPORT void JNICALL Java_android_serialport_SerialPort_native_1close (JNIEnv *, jobject); /* * Class: android_serialport_SerialPort * Method: native_read_array * Signature: ([BI)I */ JNIEXPORT jint JNICALL Java_android_serialport_SerialPort_native_1read_1array (JNIEnv *, jobject, jbyteArray, jint); /* * Class: android_serialport_SerialPort * Method: native_read_direct * Signature: (Ljava/nio/ByteBuffer;I)I */ JNIEXPORT jint JNICALL Java_android_serialport_SerialPort_native_1read_1direct (JNIEnv *, jobject, jobject, jint); ......... #ifdef __cplusplus } #endif #endifSerialPort.c : 实现SerialPort.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 "SerialPort.h" #include "android/log.h" static const char *TAG="serial_port"; #define LOGI(fmt, args...) __android_log_print(ANDROID_LOG_INFO, TAG, fmt, ##args) #define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, TAG, fmt, ##args) #define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, TAG, fmt, ##args) //static jfieldID field_context; // cannot define this , use this will anr. why?????? /* * Class: android_serialport_SerialPort * Method: native_open * Signature: (Ljava/lang/String;I)Ljava/io/FileDescriptor; */ JNIEXPORT jobject JNICALL Java_android_serialport_SerialPort_native_1open (JNIEnv *env, jclass thiz, jstring path, jint baudrate) { int fd; speed_t speed; jobject mFileDescriptor; /* Check arguments */ { speed = getBaudrate(baudrate); if (speed == -1) { /* TODO: throw an exception */ LOGE("Invalid baudrate"); jniThrowException(env, "java/io/IOException bad baudrate!!!!!", NULL); 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); LOGD("open() fd = %d", fd); // (*env)->SetIntField(env,thiz, field_context, fd); // cannot define this , use this will anr. why?????? (*env)->ReleaseStringUTFChars(env, path, path_utf); if (fd == -1) { /* Throw an exception */ LOGE("Cannot open port"); jniThrowException(env, "java/io/IOException", NULL); /* 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 */ jniThrowException(env, "java/io/IOException", NULL); return NULL; } cfmakeraw(&cfg); cfsetispeed(&cfg, speed); cfsetospeed(&cfg, speed); if (tcsetattr(fd, TCSANOW, &cfg)) { LOGE("tcsetattr() failed"); close(fd); /* TODO: throw an exception */ jniThrowException(env, "java/io/IOException", NULL); 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 */ JNIEXPORT void JNICALL Java_android_serialport_SerialPort_native_1close (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); LOGD("close(fd = %d)", descriptor); close(descriptor); } ..........
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) TARGET_PLATFORM := android-3 LOCAL_MODULE := serial_port LOCAL_SRC_FILES := SerialPort/SerialPort.c JNIHelp.cpp LOCAL_LDLIBS := -llog include $(BUILD_SHARED_LIBRARY)
第六步,将serial_port.so文件拷贝到eclipse项目 project/libs/armeabi/下,至此OK。