1. 程式人生 > >Android Studio 串列埠jni開發

Android Studio 串列埠jni開發

1.開發環境


2. 建立新工程

建立SerialPortDemo工程, Minimum SDK 選擇 API 19: Android 4.4。


3. 工程環境

安裝CMake/LLDB/NDK, Gradle Version 4.4




4. 切換工程顯示方式


5.修改build.gradle


6.修改app的build.gradle


7.建立jni目錄


SerialPort.h

#include <jni.h>

#ifndef _Included_com_imlaidian_serialportdemo_serialapi_SerialPort
#define _Included_com_imlaidian_serialportdemo_serialapi_SerialPort

#ifdef __cplusplus
extern "C" {
#endif

// 命名格式: java_包名_類目錄_類名_介面
JNIEXPORT jobject JNICALL Java_com_imlaidian_serialportdemo_serialapi_SerialPort_open
  (JNIEnv *, jclass, jstring, jint, jint);


JNIEXPORT void JNICALL Java_com_imlaidian_serialportdemo_serialapi_SerialPort_close
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif

#endif

SerialPort.c
/*
 * 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 <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <jni.h>

#include "termios.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_imlaidian_serialdemo_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_imlaidian_serialdemo_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);
}

由於api 19 之後的 termios.h 裡面的函式有調整,因此除錯過程中,出現

java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "tcgetattr" referenced by "libserialport.so"...

解決方法:

將api 19  的 termios.h 拷貝到 jni 目錄下

termios.h

/*
 * Copyright (C) 2008 The Android Open Source Project
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
#ifndef _TERMIOS_H_
#define _TERMIOS_H_

#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <stdint.h>
#include <linux/termios.h>

__BEGIN_DECLS

/* Redefine these to match their ioctl number */
#undef  TCSANOW
#define TCSANOW    TCSETS

#undef  TCSADRAIN
#define TCSADRAIN  TCSETSW

#undef  TCSAFLUSH
#define TCSAFLUSH  TCSETSF

static __inline__ int tcgetattr(int fd, struct termios *s)
{
    return ioctl(fd, TCGETS, s);
}

static __inline__ int tcsetattr(int fd, int __opt, const struct termios *s)
{
    return ioctl(fd, __opt, (void *)s);
}

static __inline__ int tcflow(int fd, int action)
{
    return ioctl(fd, TCXONC, (void *)(intptr_t)action);
}

static __inline__ int tcflush(int fd, int __queue)
{
    return ioctl(fd, TCFLSH, (void *)(intptr_t)__queue);
}

static __inline__ pid_t tcgetsid(int fd)
{
    pid_t _pid;
    return ioctl(fd, TIOCGSID, &_pid) ? (pid_t)-1 : _pid;
}

static __inline__ int tcsendbreak(int fd, int __duration)
{
    return ioctl(fd, TCSBRKP, (void *)(uintptr_t)__duration);
}

static __inline__ speed_t cfgetospeed(const struct termios *s)
{
    return (speed_t)(s->c_cflag & CBAUD);
}

static __inline__ int cfsetospeed(struct termios *s, speed_t  speed)
{
    s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);
    return 0;
}

static __inline__ speed_t cfgetispeed(const struct termios *s)
{
    return (speed_t)(s->c_cflag & CBAUD);
}

static __inline__ int cfsetispeed(struct termios *s, speed_t  speed)
{
    s->c_cflag = (s->c_cflag & ~CBAUD) | (speed & CBAUD);
    return 0;
}

static __inline__ void cfmakeraw(struct termios *s)
{
    s->c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP|INLCR|IGNCR|ICRNL|IXON);
    s->c_oflag &= ~OPOST;
    s->c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
    s->c_cflag &= ~(CSIZE|PARENB);
    s->c_cflag |= CS8;
}

__END_DECLS

#endif /* _TERMIOS_H_ */

此處參考:

8.建立串列埠native介面類


SerialPort.java

/*
 * 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.imlaidian.serialportdemo.serialapi;

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;

import android.util.Log;

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 */
		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 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, 8, 1,'n');
		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 databits,int stopbits,char parity);
	public native void close();
	static {
		System.loadLibrary("serialport"); // 呼叫jni生成的庫
	}
}


SerialPortFinder.java

/*
 * 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.imlaidian.serialportdemo.serialapi;

import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;

import android.util.Log;

public class SerialPortFinder {

	public class Driver {
		public Driver(String name, String root) {
			mDriverName = name;
			mDeviceRoot = root;
		}
		private String mDriverName;
		private String mDeviceRoot;
		Vector<File> mDevices = null;
		public 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 static final String TAG = "SerialPort";

	private Vector<Driver> mDrivers = null;

	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()]);
	}
}

9. gradle sync同步


10. 生成.so 路徑


相關推薦

Android Studio 串列jni開發

1.開發環境2. 建立新工程建立SerialPortDemo工程, Minimum SDK 選擇 API 19: Android 4.4。3. 工程環境安裝CMake/LLDB/NDK, Gradle Version 4.44. 切換工程顯示方式5.修改build.gradl

Android Studio 3.0 JNI開發入門

本入門教程使用的作業系統為ubuntu18.04 開發環境準備 在AS中新建一個專案,開啟專案的File–>Settings–>Android SDK–>SDK Tool,下載安裝CMake、LLDB、NDK。 建立一個支援C/C++的An

Android Studio 3.0 Jni 開發環境配置 ndk cmake編譯 多個C/C++檔案新增配置

為什麼要用cmake?我先說一下cmake的優點: 1.配置簡單,只需要一個text檔案幾句程式碼即可完成Jni環境配置 2.請看下圖: 第一張圖是在幹什麼?debug C/C++程式碼啊!驚不驚險刺不刺激?! 都能夠直接debug了,新增檔案新

android studio 進行ndk/jni開發

一、前言:之前用eclipse開發ndk的時候大家是不是很痛苦,要做的事情很多:     //NDK開發流程:      1、在java程式碼中 宣告本地方法(native)     2、通過javah工具完成jni樣式的標頭

Android 串列通訊開發筆記3

Android串列埠開發 延伸和擴充套件,1.使用JNI Cmake 自己編譯串列埠通訊 的so庫:Android Studio 3.0 實現方式。2.CRC校驗 以及擴充套件設計:a.一(串列埠)對多(硬體通訊);b.多(串列埠)對多(硬體)的實現。1.以串列埠除錯工具為例,使用其原本的原始碼使用JNI C

Android USB轉串列開發(hoho.android.usbserial串列庫)

使用hoho.android.usbserial串列埠庫開發串列埠 import android.app.Application import android.app.PendingIntent import android.content.BroadcastReceiver import

Android studio 百度地圖開發(2)地圖定位

gcj02 settings tick all adding ext tope wid erro Android studio 百度地圖開發(2)地圖定位 email:[email protected]/* */ 開發環境:win7 64位

android studio ndk-builld方式開發

項目 har 自己 eve mono 搜索路徑 平臺 bsp ide 之前都是在Ubuntu開發,項目也是老的,自然也就順理成章的用eclipse做各種android的開發。最近想在android studio 切換下,有點不習慣。android studio 為ndk開發

MTK串列驅動開發

MTK串列埠驅動開發 由於最近在工作中需要使用MTK的MT6261進行移動嵌入式裝置的開發,所以將MTK串列埠驅動開發流程貼出來分享給大家。 1.使用串列埠工具配置UART管腳,此處配置的是UART2開啟原始碼目錄下的\custom\drv\Drv_Tool\DrvGen.exe

Android串列程式設計

閱讀時長:10分鐘 原文地址:juejin.im/post/5bd96c… 原文作者:YKamh 技術預備:Java基礎 如今我們生活中充滿了各種智慧裝置,方便了我們的生活,這正是物聯網時代。如果我們要開發智慧裝置,那麼Android串列埠程式設計是我們應該必備的技能。 在投身到An

安卓學習筆記 -- (安裝環境) Android Studio安裝配置、環境搭建詳細步驟及基本使用 Android Studio和SDK官方開發工具下載 Android Studio教程從入門到精通 Android開發-之第一個程式:HelloWorld!

1、下載Android Studio安裝配置、環境搭建詳細步驟及基本使用    https://www.cnblogs.com/yanglh6-jyx/p/Android_AS_Configuration.html https://blog.csdn.net/k491022087/ar

Android平臺 串列232通訊

開發串列埠程式首先要求你的裝置需要支援串列埠通訊,可以在裝置上裝一個App端的串列埠工具來檢測一下http://dl.pconline.com.cn/download/1214519.html,或者在電腦端下載一個友善串列埠助手檢測一下,一般在Android工控主機板上面都會帶有串列埠。 首

Android Studio 2.2 ndk開發環境 gradle配置

AndroidStudio2.2中 對於ndk/jni 建議使用cmake進行編譯 而對於純粹的android程式設計師來說,cmake不是經常接觸的方案,我在遇到jni相關時也遇到了一些問題,這裡拋磚引玉吧 開發環境: Android SDK/NDK Android St

串列通訊開發

一開始做串列埠通訊開發時,覺得並不難,無非就是傳送,然後等一會,再接收就完事了。其實裡面的水很深,特別是在各種裝置都有的情況下。我們在整個開發過程中,遇到了以下的幾個主要問題: 1、裝置出現嚴重的延遲。 2、接收過程出現數據粘包或截斷。 3、多裝置共用一個串列埠。 4、使用RTU的情

Android Studio中進行NDK開發的一般流程

1 在類中宣告native方法 2 在 app/src/main 下建立 jni 目錄 3 在 app/src/main/java 下執行命令 javah -jni -d ../jni com.path2class.ClassName 4 在 app/src/main/jni

Android Studio上搭建OpenCV 開發環境

        這裡預設你已經成功安裝了Android Studio IDE;我這裡使用的AS版本是3.0.1;截止目前,AS穩定版本已經升級至3.2.1,至於版本間的區別,這裡不多做區分和說明。如果對於AS的下載和安裝有問題也可以參加我的關於AS介紹的部落格

Android USB串列攝像頭實現拍照與識別二維碼

二維碼現在用的超級多,其實它就是一種編碼,把字串編碼儲存成一個圖片,我們掃描圖片得到字串就解碼成功。 最有名的二維碼解析庫是 google 出品的 Zxing,網上也有很多的封裝庫,有自定義掃描視窗等,更多庫請在這個庫中搜索 二維碼即可   USBUVCCa

Android Studio 3.0 JNI的實現

當前環境:  android studio 3.0.0  android-ndk版本: 1、建立android專案 (JNIDemo) 2、建立jni資料夾 右鍵 src->New->Folder->JNI Folder 如圖: 結

谷歌Android開源串列通訊使用

Demo下載地址: 谷歌官方串列埠庫使用 引言: 現在的串列埠通訊多用於嵌入裝置中,Android主機板與各種板卡之間的通訊。因此串列埠通訊在未來智慧裝置中應用會很廣泛。 現在市面上幾乎所有的Android串列埠通訊庫都是用的Google開源的https://github.

Android Studio 3.2 JNI (ndk-build)

記錄下 Android Studio 嵌入 C 程式碼的過程,使用 ndk-build. 當前環境: Android Studio 3.2 NDK 18.1 建立 JNI 資料夾 直接在專案右鍵,選擇 New - Folder - JNI Folder ,對話方塊直接點選