JIN编程经验分享

目前为止 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编程经验分享至此,有疑问的欢迎咨询,随时解答

 

你可能感兴趣的