目前为止 jni 编程主要用于串口通信,一般有RS485 RS232等,这两种串口C代码网上都有,
//
// Created by Administrator on 2017/5/31.
//
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define GPIO_IOC_MAC 'L'
#define CMD_ctl485 _IO(GPIO_IOC_MAC,1)
#define CMD_ctlled _IOR(GPIO_IOC_MAC,2,int)
#define CMD_ctlkey _IOW(GPIO_IOC_MAC,3,int)
#include "com_senthink_portablebattery_serialport_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 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;
}
}
JNIEXPORT jobject JNICALL Java_com_senthink_portablebattery_serialport_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;
}
JNIEXPORT void JNICALL Java_com_senthink_portablebattery_serialport_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);
}
此文件只有打开串口和关闭串口两个功能,如果需要新增功能就需要C语言同事帮忙编写,并且在fragment底层打开新功能的接口。
1...jni编程第一步:升级成class文件后生成.h文件
cd到包含native方法的包名下 执行javac SerialPort.java,在该包下会生成SerialPort.class文件
cd到java目录下,这样生成的.h也会在java目录下 cd app\src\main\app
然后执行javah编译头文件,规则是javah -jni 包名+包含native方法的类名 javah -jni com.example.demo.SerialPort
2...将生成的头文件和c文件同事放到一个新的包中 如:app\libs下面
然后创建 Android.mk 和 Application.mk文件
Android.mk 设置生成.so库的名字,以后system.loadLibrary使用:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
TARGET_PLATFORM := android-3
LOCAL_MODULE := SerialPort
LOCAL_SRC_FILES := SerialPort.c
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
Application.mk生成.so库的类型
APP_ABI := armeabi armeabi-v7a x86
3...最重要的一步 生成so文件
使用Terminal cd到新建的那个包下 我的是 app\lib,然后执行ndk-build
此时app\lib下就生成了so文件,将libs下的包拷贝到你自己的lib文件中即可,obj文件可删除
下面附串口功能类,SerialHelper,主要负责打开串口并进行读写功能,读的回调在ReadThread中,onDataReceived();
package com.senthink.portablebattery.serialport;
import android.annotation.SuppressLint;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
import com.senthink.portablebattery.Utils.SaveLogUtil;
import com.senthink.portablebattery.serialport.bean.ComBean;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
public abstract class SerialHelper {
public static final String TAG = "==SerialHelper==";
private SerialPort mSerialPort;
private OutputStream mOutputStream;
private InputStream mInputStream;
private static volatile ReadThread mReadThread;
private SendThread mSendThread;
public String sPort;
public int iBaudRate;
private boolean _isOpen = false;
private byte[] _bLoopData = new byte[]{0x30};
private int iDelay = 1000;
public SerialHelper(String sPort, int iBaudRate) {
this.sPort = sPort;
this.iBaudRate = iBaudRate;
}
public void open() throws SecurityException, IOException, InvalidParameterException {
mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0);
mOutputStream = mSerialPort.getOutputStream();
mInputStream = mSerialPort.getInputStream();
mReadThread = new ReadThread();
mReadThread.start();
// mSendThread = new SendThread();
// mSendThread.setSuspendFlag();
// mSendThread.start();
_isOpen = true;
}
public void close() {
if (mReadThread != null)
mReadThread.interrupt();
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
_isOpen = false;
}
public void send(byte[] bOutArray) {
try {
mOutputStream.write(bOutArray);
handler.sendEmptyMessageDelayed(4856, 100);
} catch (IOException e) {
SaveLogUtil.saveLog(TAG, "====发送串口数据异常===" + e.getMessage());
e.printStackTrace();
}
}
public boolean setBaudRate(int iBaud) {
if (_isOpen) {
return false;
} else {
iBaudRate = iBaud;
return true;
}
}
public boolean setPort(String sPort) {
if (_isOpen) {
return false;
} else {
this.sPort = sPort;
return true;
}
}
public boolean isOpen() {
return _isOpen;
}
public byte[] getbLoopData() {
return _bLoopData;
}
public void sendHex(String sHex) {
byte[] bOutArray = MyFunc.HexToByteArr(sHex);
send(bOutArray);
}
public void sendTxt(String sTxt) {
byte[] bOutArray = sTxt.getBytes();
send(bOutArray);
}
private class ReadThread extends Thread {
@Override
public void run() {
Looper.getMainLooper();
Looper.prepare();
while (!interrupted()) {
try {
if (mInputStream == null) {
SaveLogUtil.saveLog(TAG, "==串口输入流为空==");
} else {
int s = mInputStream.available();
if (s > 0) {
byte[] buffer = new byte[s];
if (mInputStream.read(buffer) != -1) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < buffer.length; i++) {
stringBuilder.append(MyFunc.Byte2Hex(buffer[i])).append(" ");
}
ComBean ComRecData = new ComBean(sPort, stringBuilder.toString());
onDataReceived(ComRecData);
}
}
}
Thread.sleep(50);
// SaveLogUtil.saveLog("tag", "" + System.currentTimeMillis());
} catch (Exception e) {
SaveLogUtil.saveLog(TAG, "==读取串口数据异常==");
e.printStackTrace();
}
}
}
}
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
}
};
private class SendThread extends Thread {
public boolean suspendFlag = true;
@Override
public void run() {
super.run();
while (!isInterrupted()) {
synchronized (this) {
while (suspendFlag) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Log.e("===============", "==========sendThread====");
send(getbLoopData());
try {
Thread.sleep(iDelay);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void setSuspendFlag() {
this.suspendFlag = true;
}
public synchronized void setResume() {
this.suspendFlag = false;
notify();
}
}
public int getBaudRate() {
return iBaudRate;
}
public boolean setBaudRate(String sBaud) {
int iBaud = Integer.parseInt(sBaud);
return setBaudRate(iBaud);
}
public String getPort() {
return sPort;
}
public void setbLoopData(byte[] bLoopData) {
this._bLoopData = bLoopData;
}
public void setTxtLoopData(String sTxt) {
this._bLoopData = sTxt.getBytes();
}
public void setHexLoopData(String sHex) {
this._bLoopData = MyFunc.HexToByteArr(sHex);
}
public int getiDelay() {
return iDelay;
}
public void setiDelay(int iDelay) {
this.iDelay = iDelay;
}
public void startSend() {
if (mSendThread != null) {
mSendThread.setResume();
}
}
public void stopSend() {
if (mSendThread != null) {
mSendThread.setSuspendFlag();
}
}
protected abstract void onDataReceived(ComBean ComRecData);
}
SerialPort 类就是包含native的方法,可以根据需求自己增加其他native方法
package com.senthink.portablebattery.serialport;
import android.util.Log;
import com.senthink.portablebattery.Utils.SaveLogUtil;
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;
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 {
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
// JNI
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
// public native static void open485(int flags);
// public native static int getPhoneState();
// public native static void setPhoneLight(int light);
}
个人JNI编程经验分享至此,有疑问的欢迎咨询,随时解答