这是参考谷歌官网的开源项目和网上的串口通讯写的一个简单的demo。刚刚接触NDK编程以及串口通讯,可能很多写的不是很好,不喜勿喷。
大多数的的代码都在这个Google串口开源项目:https://code.google.com/p/android-serialport-api/
先新建一个带有c的项目。
勾选C++然后一路Next新建。
在编写代码前或者安装该项目前先将该项目使用的NDk换为NDK14版本,不然可能会出现串口打不开问题(本人现在不知道为什么,可能是环境配置问题),没有NDK14的先下载(在https://developer.android.google.cn/ndk/downloads/older_releases.html下载),然后在Android视图下,app——OPen Module Settings——SDK Location中重新选择NDK的路径。
将MainActivity中
static {
System.loadLibrary("native-lib");
}
的删除,在创建SerialPort类中添加
//在应用程序启动时,用于加载serial_port库
static {
System.loadLibrary("serial_port");//serial_port根据后面的CMakeLists.txt内容写 。
}
将创建项目时的Cpp文件删除,再新建一个jni文件夹,然后在该文件夹中新建serial_port的.c文件 调用串口的读写主要就是open和close方法,类似于文件读写。
按照下面代码中的注释修改后,左边的绿红箭头表示java中的open方法和c中的open方法建立了联系。
#include <jni.h>
#include <termios.h>
#include <unistd.h>
#include <fcntl.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 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;
default: return -1;
}
}
/*
* Java_com_smartlab_blue_serialdemo_SerialPort_open中的com_smartlab_blue_serialdemo是包名
* SerialPort_open是SerialPort类中的private native static FileDescriptor open(...)方法,需要用"_"连接不能用"."
* 有的可能直接在SerialPort类中进行Alt+Enter就可以在这个文件里面创建一个open方法,只需把这个类中我写的方法名换一下就行了,
* 有的就需要按照我上面说的方法改方法名字。 下面的close方法配置一样
* */
JNIEXPORT jobject JNICALL
Java_com_smartlab_blue_serialdemo_SerialPort_open(JNIEnv *env, jclass type, jstring path, jint baudrate, jint flags) {
int fd;
speed_t speed; //引用#include <termios.h>
jobject mFileDescriptor;
/* Check arguments */
{
speed=getBaudrate(baudrate);
if (speed==-1){
LOGE("Invalid baudrate");
return NULL;
}
}
/* 开启 设备 */
{
jboolean iscapy;
const char *path_utf = (*env)->GetStringUTFChars(env, path, &iscapy);
LOGD("Opening serial port %s with flags 0x%x", path_utf, O_RDWR | flags);
fd=open(path_utf,O_RDWR | flags | O_NDELAY);//O_RDWR 引入 #include <fcntl.h>
LOGD("open() fd = %d", fd);
(*env)->ReleaseStringUTFChars(env, path, path_utf );
if (fd==-1){
/* 引发异常 */
LOGE("Cannot open port");
/* TODO: throw an exception */
return NULL;
}
}
/* 配置 设备 */
{
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;
}
}
/* 创建一个相应的文件描述符 */
{
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;
}
JNIEXPORT void JNICALL
Java_com_smartlab_blue_serialdemo_SerialPort_close(JNIEnv *env, jobject instance) {
jclass SerialPortClass = (*env)->GetObjectClass(env, instance);
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, instance, mFdID);
jint descriptor = (*env)->GetIntField(env, mFd, descriptorID);
LOGD("close(fd = %d)", descriptor);
close(descriptor);
}
先将app 的build,gradle中的 以下内容 注释
//externalNativeBuild {
// cmake {
// path "CMakeLists.txt"
// }
//}
在创建该项目时生成的的CMakeLists.txt中进行以下修改
add_library( # Sets the name of the library.
serial_port //双引号中写的是这里的名字,在应用程序启动时,用于加载serial_port库
# Provides a relative path to your source file(s).
src/main/jni/serial_port.c //你新建的.c文件的路径和名字。
target_link_libraries( # Specifies the target library.
serial_port //双引号中写的是这里的名字
app 的build,gradle中添加一下内容
defaultConfig {
applicationId “com.smartlab.blue.serialdemo”
minSdkVersion 14
targetSdkVersion 26
versionCode 1
versionName “1.0”
testInstrumentationRunner “android.support.test.runner.AndroidJUnitRunner”
//添加
ndk {
moduleName "serial_port" ldLibs "log", "z", "m"
}
}
切换成Android视图在app上右键,然后选择Link C++ Project with Gradle
第一个选择CMake,路径选择刚刚修改的CMakeLists.txt,然后OK。
注意:有时候可能无法开启Android板上串口读写权限,需要自己去手动的添加权限
在调试中如果大家没有串口硬件的话可以使用PC机+模拟器完成调试实验。具体操作方法:
1、打开cmd进入到android开发的sdk目录下的tools文件夹下;
2、执行命令emulator @(你自己的模拟器的名字) -qemu -serial COM3(COM3是你的PC的一个串口通过命令挂载到你的模拟器上,当然你也可以是COM1跟你电脑对应);例:我的模拟器叫123,我给模拟器挂载的电脑上的串口是COM3,则执行:emulator @123 -qemu -serial COM3
这样你会看到模拟器已经启动,这时候你将程序部署上运行即可。
如果用程序打开串口,提示没有读写权限。应该在命令提示符下用linux命令赋予读写的权限: 进入shell:adb shell 进入设备目录:#cd dev 修改权限:#chmod 777 ttyS2即可。(这个我已经写入程序内,无需在添加,如果LOG日志打印显示无权限,那就只能你手动在执行一边,再解说以下这个进入shell,这个是adb shell,你的命令执行的目录下必须有adb shell.exe多的不解释了)
修改的地方基本上就上面那些,推荐一遍博客http://blog.csdn.net/qq_35071078/article/details/73065046,不知道串口的可以看下 。
下面是主要的代码
public class SerialPort {
private static final String TAG = "SerialPort";
private FileDescriptor mFd;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate, int flags)
throws SecurityException, IOException {
/* 检查是否又读写的权限 */
if (!device.canRead() || !device.canWrite()) {
/* 如果没有读写的权限, 尝试使用chmod来给文件权限 */
Process su = null;
try {
su = Runtime.getRuntime().exec("/system/xbin/su");
//su = Runtime.getRuntime().exec("su");
String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
Log.e(TAG, "su.waitFor == " + su.waitFor() + " device.canRead = " + device.canRead() + " device.canWrite = " + device.canWrite());
throw new SecurityException();
}
} catch (Exception e) {
Log.e(TAG, "Exception == " + e.toString());
e.printStackTrace();
throw new SecurityException();
} finally {
if (su != null) {
su.destroy();
}
}
}
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);
Log.e(TAG, "****************** 串 口 打 开 成 功 !**********************");
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
/**
* jni方法 打开串口
*
* @param path 串口文件的路径
* @param baudrate 波特率
* @param flags 端口
* @return 文件描述符
*/
private native static FileDescriptor open(String path, int baudrate,
int flags);
/**
* jni方法 关闭串口
*/
public native void close();
//在应用程序启动时,用于加载serial_port库
static {
System.loadLibrary("serial_port");
}
}
public abstract class SerialPortActivity extends Activity {
protected Application mApplication;
protected SerialPort mSerialPort;
protected OutputStream mOutputStream;
private InputStream mInputStream;
private ReadThread mReadThread;
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
while(!isInterrupted()) {
int size;
try {
byte[] buffer = new byte[64];
if (mInputStream == null) return;
Log.e("读取数据的线程","233333");
size = mInputStream.read(buffer);
if (size > 0) {
onDataReceived(buffer, size);
}
} 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 DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
SerialPortActivity.this.finish();
}
});
b.show();
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mApplication = (Application) getApplication();
try {
mSerialPort = mApplication.getSerialPort();
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(final byte[] buffer, final int size);
@Override
protected void onDestroy() {
if (mReadThread != null)
mReadThread.interrupt();
mApplication.closeSerialPort();
mSerialPort = null;
super.onDestroy();
}
}
public class SerialPortFinder {
private static final String TAG = "SerialPort";
private Vector<Driver> mDrivers = null;
private class Driver {
private Driver(String name, String root) {
mDriverName = name;
mDeviceRoot = root;
}
private String mDriverName;
private String mDeviceRoot;
Vector<File> mDevices = null;
private Vector<File> getDevices() {
if (mDevices == null) {
mDevices = new Vector<File>();
File dev = new File("/dev");
File[] files = dev.listFiles();
int i;
for (i = 0; i < files.length; i++) {
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
Log.d(TAG, "Found new device: " + files[i]);
mDevices.add(files[i]);
}
}
}
return mDevices;
}
public String getName() {
return mDriverName;
}
}
private Vector<Driver> getDrivers() throws IOException {
if (mDrivers == null) {
mDrivers = new Vector<Driver>();
LineNumberReader r = new LineNumberReader(new FileReader(
"/proc/tty/drivers"));
String l;
while ((l = r.readLine()) != null) {
// Issue 3:
// Since driver name may contain spaces, we do not extract
// driver name with split()
String drivername = l.substring(0, 0x15).trim();
String[] w = l.split(" +");
if ((w.length >= 5) && (w[w.length - 1].equals("serial"))) {
Log.d(TAG, "Found new driver " + drivername + " on "
+ w[w.length - 4]);
mDrivers.add(new Driver(drivername, w[w.length - 4]));
}
}
r.close();
}
return mDrivers;
}
public String[] getAllDevices() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while (itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while (itdev.hasNext()) {
String device = itdev.next().getName();
String value = String.format("%s (%s)", device,
driver.getName());
devices.add(value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
public String[] getAllDevicesPath() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while (itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while (itdev.hasNext()) {
String device = itdev.next().getAbsolutePath();
devices.add(device);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
}