這部分源碼一直沒變過,不過如今Android Studio已經支持Cmake方式了,因此不須要單獨編譯動態連接庫了,能夠直接修改Cmake文件和C文件html
開源庫: https://github.com/cepr/android-serialport-apijava
通過以上3步,基本工做就作完了,這時候你的cpp庫有3個文件: CMakeLists.txt 文件, c文件和頭文件。android
而後剩下的就是定義調用串口的文件了ios
如下是 SerialPort.c 文件,除了輸出的 JNICALL 基本沒啥要改的git
- 格式是
Java_包名_類名_方法名
,用下劃線隔開- 下面要建立的java類及方法必定要嚴格對應
- 頭文件和C代碼的 JNICALL 保持同名
/* * Copyright 2009-2011 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #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 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_SerialPort * Method: open * Signature: (Ljava/lang/String;II)Ljava/io/FileDescriptor; */ JNIEXPORT jobject JNICALL Java_com_jiataoyuan_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, "<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_com_jiataoyuan_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); }
add_librarygithub
- 1參設置庫名(你要在java中加載的庫名稱),
- 2參設置共享(基本都是SHARE),
- 3參設置源文件的相對路徑(由於在同一目錄下,直接寫文件名便可)
find_libraryweb
這個東西我也沒搞明白,看說明是用來搜索庫路徑的,可是由於有默認值,因此只須要指定NDK就好了,並且刪了也不影響什麼express
target_link_librariesapache
- 1參指定目標庫,就是上面設置的庫
- 2參用來鏈接到日誌庫,已經包含在nkd中了,若是你的 find_library 默認的話,這裏也默認就好了,若是 find_library 刪掉的話,這裏也刪掉
# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the native library. cmake_minimum_required(VERSION 3.4.1) # Creates and names a library, sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( # Sets the name of the library. SerialPort # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). SerialPort.c) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. SerialPort # Links the target library to the log library # included in the NDK. ${log-lib})
這個文件我改了一點,變化不大,你也能夠直接用開源庫中的,主要內容以下:api
- 獲取串口:改動部分爲將可讀寫改成可讀寫可執行
- 獲取讀寫流
- 設置jni方法 ,打開和關閉,其中打開方法是私有方法
/* * Copyright 2009 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jiataoyuan.serialport; 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; public class SerialPort { private static final String TAG = "SerialPort"; static { System.loadLibrary("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; // 輸出流 /** * 獲取串口 * * @param device 設備 * @param baudrate 波特率 * @param flags 標誌符 * @throws SecurityException * @throws IOException */ public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException { /* 檢查訪問權限 */ if (!device.canRead() || !device.canWrite()) { try { /* Missing read/write permission, trying to chmod the file */ Process su; su = Runtime.getRuntime().exec("/system/bin/su"); String cmd = "chmod 777 " + 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(); } } // 若是打不開返回null 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); } // 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(); }
爲啥封裝成這樣子,由於個人串口調試助手,因此最後改爲這樣子了,通俗易懂
/* * Copyright 2009 Cedric Priscal * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.jiataoyuan.serialport; import android.app.Activity; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.os.Bundle; import com.jiataoyuan.dronetrack.R; import com.jiataoyuan.dronetrack.utils.MyConstantUtils; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.security.InvalidParameterException; public abstract class SerialPortActivity extends Activity { protected SerialPort mSerialPort; protected OutputStream mOutputStream; private InputStream mInputStream; private ReadThread mReadThread; // 根據須要修改線程 private class ReadThread extends Thread { @Override public void run() { super.run(); int size; byte[] buffer = new byte[64]; StringBuilder sb = new StringBuilder(); while (!isInterrupted()) { try { if ((size = mInputStream.read(buffer)) != -1) { sb.append(new String(buffer, 0, size)); onDataReceived(sb.toString()); } } catch (IOException e) { e.printStackTrace(); return; } } } } // 打印錯誤消息彈框 private void DisplayError(int resourceId) { AlertDialog.Builder b = new AlertDialog.Builder(this); b.setTitle("Error"); b.setMessage(resourceId); b.setPositiveButton("OK", new OnClickListener() { public void onClick(DialogInterface dialog, int which) { SerialPortActivity.this.finish(); } }); b.show(); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); try { if (null == mSerialPort) { // 1參 串口 根據你的串口狀況填入,如"/dev/ttyS4" , "/dev/ttyS1" // 2參 波特率 根據約定填入,如19200 ,115200 // 3參 標誌位 根據約定填入,沒有填0 mSerialPort = new SerialPort(new File(MyConstantUtils.SerialPortPath), MyConstantUtils.baudrate, 0); } mOutputStream = mSerialPort.getOutputStream(); mInputStream = mSerialPort.getInputStream(); /* Create a receiving thread */ mReadThread = new ReadThread(); mReadThread.start(); } catch (SecurityException e) { DisplayError(R.string.error_security); } catch (IOException e) { DisplayError(R.string.error_unknown); } catch (InvalidParameterException e) { DisplayError(R.string.error_configuration); } } // 定義接收的抽象方法 protected abstract void onDataReceived(String buffer); // 發送消息 public void sendMsg(byte[] msg) { try { mOutputStream = mSerialPort.getOutputStream(); if (msg.length > 0) { mOutputStream.write(msg); mOutputStream.flush(); } } catch (IOException e) { e.printStackTrace(); } } // 關閉端口 public void closeSerialPort() { if (mSerialPort != null) { mSerialPort.close(); mSerialPort = null; } } @Override protected void onDestroy() { if (mReadThread != null) mReadThread.interrupt(); closeSerialPort(); mSerialPort = null; super.onDestroy(); } }
要調用的Activity須要繼承 SerialPortActivity ,並實現 onDataReceived 方法
收
@Override protected void onDataReceived(String buffer) { mBuffer = buffer; Handler handler = new Handler(Looper.getMainLooper()); handler.postDelayed(runnable, 100); }
發
private void send(String msg) { sendMsg(msg.getBytes()); } private void send(byte[] msg) { sendMsg(msg); L.e(new String(msg)); }
開關串口都在 SerialPortActivity
的create和destroy方法中調用了,應用邏輯無需管理