[Android jni开发系列(四)]利用jni开发生成Android串口开发的so库
16lz
2021-01-23
1、定义包含native方法的java类
package android.serialport.api;import android.util.Log;import java.io.File;import java.io.FileDescriptor;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;/** * Create by Jason.Yin 2018/4/24. */public class SerialPort { private static final String TAG = "SerialPort"; /* Do not remove or rename the field mFd: it is used by native method close(); */ private FileDescriptor mFd; private FileInputStream mFileInputStream; private FileOutputStream mFileOutputStream; public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { /* Check access permission, the serial port must can be write and read */ if (!device.canRead() || !device.canWrite()) { try { Process su; su = Runtime.getRuntime().exec("/system/bin/su"); String cmd = "chmod 666 " + device.getAbsolutePath() + "\n" + "exit\n"; su.getOutputStream().write(cmd.getBytes()); if ((su.waitFor() != 0) || !device.canRead() || !device.canWrite()) { throw new SecurityException(); } } catch (Exception e) { e.printStackTrace(); throw new SecurityException(); } } mFd = open(device.getAbsolutePath(), baudrate, flags); if (mFd == null) { Log.e(TAG, "native open returns null"); throw new IOException(); } mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); } public InputStream getInputStream() { return mFileInputStream; } public OutputStream getOutputStream() { return mFileOutputStream; } //Definition jni method private native static FileDescriptor open(String path, int baudrate, int flags); public native void close(); static { System.loadLibrary("serial_port"); }}
2、利用《[Android jni开发系列(二)]AndroidStudio移植eclipse NDK项目开发》中定义的javah命令生成jni的头文件
android_serialport_api_SerialPort.h/* DO NOT EDIT THIS FILE - it is machine generated */#include /* Header for class android_serialport_api_SerialPort */#ifndef _Included_android_serialport_api_SerialPort#define _Included_android_serialport_api_SerialPort#ifdef __cplusplusextern "C" {#endif/* * Class: android_serialport_api_SerialPort * Method: open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */JNIEXPORT jobject JNICALL Java_android_serialport_api_SerialPort_open (JNIEnv *, jclass, jstring, jint, jint);/* * Class: android_serialport_api_SerialPort * Method: close * Signature: ()V */JNIEXPORT void JNICALL Java_android_serialport_api_SerialPort_close (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
3、实现android_serialport_api_SerialPort.h中的方法
//// Created by Jason.Yin on 2018/4/11.///* DO NOT EDIT THIS FILE - it is machine generated */#include #include #include #include #include #include #include #include"android_serialport_api_SerialPort.h"#include 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 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: android_serialport_api_SerialPort * Method: open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */JNIEXPORT jobject JNICALL Java_android_serialport_api_SerialPort_open (JNIEnv *env, jclass thiz, jstring path, jint baudrate, jint flags) { int fd; speed_t speed; jobject mFileDescriptor; /* 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 with flags 0x%x", path_utf, O_RDWR | flags); fd = open(path_utf, O_RDWR | flags); 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, "" , "()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: android_serialport_api_SerialPort * Method: close * Signature: ()V */JNIEXPORT void JNICALL Java_android_serialport_api_SerialPort_close (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);}
4、配置gradle.properties和build.gradle文件
gradle.propertiesandroid.useDeprecatedNdk=true //添加这行
build.gradle defaultConfig { ... ndk { moduleName "serial_port" ldLibs "log", "z", "m" //log依赖// abiFilters "armeabi", "armeabi-v7a", "x86" //ndk 版本不能用最新的,本人测试android-ndk-r15c可以,其它的没试// abiFilters "armeabi-v7a", "x86" //ndk版本17.0.4640043 rc1不支持armeabi,否则编译会出错 } }
5、编译工程后会在下图所示目录生成对应so库
更多相关文章
- Android统一依赖多个不同版本的Support v7包
- [置顶] 找到一个在Android上创建阻塞式模态对话框的方法
- Android来电拦截的实现方法
- Android Studio集成Library时报错,终极解决方法
- 【Android UI】自定义圆形Loading动画
- Android从服务器获取图片的实例方法
- android屏幕休眠和唤醒两种方法(newWakeLock)
- 自定义ListView4