diff --git a/README.md b/README.md index aa0afce..f54c0ec 100644 --- a/README.md +++ b/README.md @@ -1,51 +1,82 @@ # Android-SerialPort-API +[Fork](https://code.google.com/archive/p/android-serialport-api/)自Google开源的Android串口通信Demo,修改成Android Studio项目 -[![](https://jitpack.io/v/licheedev/Android-SerialPort-API.svg)](https://jitpack.io/#licheedev/Android-SerialPort-API) +This lib is a [fork](https://code.google.com/archive/p/android-serialport-api/) of the Android serial port communication Demo open sourced by Google. -**Gradle 引用** +## Installation & Usage +**Gradle** -1. 在根build.gradle中添加 +添加依赖: + +Add the dependency: ``` allprojects { repositories { ... - maven { url 'https://jitpack.io' } + jcenter() + mavenCentral() // since 2.1.3 } } -``` - -2. 子module添加依赖 -``` dependencies { - implementation 'com.github.licheedev:Android-SerialPort-API:2.0.0' + // 传统4KB内存页面版本 + implementation 'com.licheedev:android-serialport:2.1.4' + // 适配16KB页面版本,https://developer.android.google.cn/guide/practices/page-sizes?hl=zh-cn + implementation 'com.licheedev:android-serialport:2.1.5' } ``` -**修改`su`路径** +**Import** + +```java +import android.serialport.SerialPort; +``` + +**`su` path** + +In order to read/write to a serial port in Android you'll need `su` binary installed on device (this can be done by rooting the device). Usually Android devices that has the ability to communicate with serial ports have `su` installed on the default path `"/system/bin/su"`. To change this use: ```java // su默认路径为 "/system/bin/su" +// The default path of su is "/system/bin/su" // 可通过此方法修改 +// If the path is different then change it using this SerialPort.setSuPath("/system/xbin/su"); ``` -**可选配置数据位、校验位、停止位** - -实现方式参考 -> https://juejin.im/post/5c010a19e51d456ac27b40fc +**Usage** ```java - // 默认8N1(8数据位、无校验位、1停止位) -SerialPort serialPort = SerialPort.newBuilder(path, baudrate).build(); - -// 7E2(7数据位、偶校验、2停止位) -SerialPort serialPort = SerialPort // - .newBuilder(path, baudrate) // 串口地址地址,波特率 - .parity(2) // 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) - .dataBits(7) // 数据位,默认8;可选值为5~8 - .stopBits(2) // 停止位,默认1;1:1位停止位;2:2位停止位 +// Default 8N1 (8 data bits, no parity bit, 1 stop bit) +SerialPort serialPort = new SerialPort(path, baudrate); + +// 可选配置数据位、校验位、停止位 - 7E2(7数据位、偶校验、2停止位) +// or with builder (with optional configurations) - 7E2 (7 data bits, even parity, 2 stop bits) +SerialPort serialPort = SerialPort + .newBuilder(path, baudrate) +// 校验位;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) +// Check bit; 0: no check bit (NONE, default); 1: odd check bit (ODD); 2: even check bit (EVEN) +// .parity(2) +// 数据位,默认8;可选值为5~8 +// Data bit, default 8; optional value is 5~8 +// .dataBits(7) +// 停止位,默认1;1:1位停止位;2:2位停止位 +// Stop bit, default 1; 1:1 stop bit; 2: 2 stop bit +// .stopBits(2) .build(); + +// read/write to serial port - needs to be in different thread! +InputStream in = serialPort.getInputStream(); +OutputStream out = serialPort.getOutputStream(); + +// close +serialPort.tryClose(); ``` + +实现方式参考 + +Implementation reference +1. Check [sample project](https://github.com/licheedev/Android-SerialPort-API/tree/master/sample) +2. https://juejin.im/post/5c010a19e51d456ac27b40fc diff --git a/build.gradle b/build.gradle index 4b0c6dd..a47094e 100644 --- a/build.gradle +++ b/build.gradle @@ -4,12 +4,13 @@ buildscript { repositories { google() jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.1' - classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' + classpath 'com.android.tools.build:gradle:4.2.2' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files + classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.32' } } @@ -17,6 +18,7 @@ allprojects { repositories { google() jcenter() + mavenCentral() } } @@ -25,11 +27,11 @@ task clean(type: Delete) { } ext { - compileSdkVersion = 28 + compileSdkVersion = 29 minSdkVersion = 8 - targetSdkVersion = 28 + targetSdkVersion = 29 - versionCode = 2 - versionName = "2.0.0" + versionCode = 3 + versionName = "2.1.5" } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index aa62a10..567806c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -13,4 +13,6 @@ # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true #Mon Sep 18 19:30:46 CST 2017 +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8afa263..3ed5e54 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Jul 03 10:00:36 CST 2019 +#Fri Sep 06 10:04:58 CST 2024 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip diff --git a/jitpack.gradle b/jitpack.gradle deleted file mode 100644 index 3e87c62..0000000 --- a/jitpack.gradle +++ /dev/null @@ -1,30 +0,0 @@ -apply plugin: 'com.github.dcendents.android-maven' - -group = 'com.github.licheedev' - -tasks.withType(JavaCompile) { - options.encoding = "UTF-8" -} - -task sourcesJar(type: Jar) { - from android.sourceSets.main.java.srcDirs - classifier = 'sources' -} - -task javadoc(type: Javadoc) { - failOnError false - source = android.sourceSets.main.java.sourceFiles - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - classpath += configurations.compile -} - - -task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir -} - -artifacts { - archives sourcesJar - archives javadocJar -} \ No newline at end of file diff --git a/maven_publish.gradle b/maven_publish.gradle new file mode 100644 index 0000000..e8a2281 --- /dev/null +++ b/maven_publish.gradle @@ -0,0 +1,130 @@ +// 依赖信息 +def groupIdDefined = "com.licheedev" +def artifactIdDefined = "android-serialport" +// 如果是测试版,版本号后面加上 -SNAPSHOT +def versionDefined = rootProject.ext.versionName +// 其他信息 +def gitUrl = "https://github.com/licheedev/Android-SerialPort-API" + +// 配置是否上传 +def toUpload = true + +//在根build.gradle中加入,最新版本号参考 https://github.com/Kotlin/dokka#using-dokka +//classpath 'org.jetbrains.dokka:dokka-gradle-plugin:1.4.32' + +//在module的build.gradle末位加入 +//apply from: '../maven_publish.gradle' + +if (toUpload) { + apply plugin: 'signing' + apply plugin: 'maven-publish' + apply plugin: 'org.jetbrains.dokka' + + // + // 打包源码 + task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' + } + + task javadoc(type: Javadoc) { + failOnError false + source = android.sourceSets.main.java.sourceFiles + options { + encoding = "utf-8" + charSet 'UTF-8' + } + classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) + classpath += configurations.compile + } + + // 打包javadoc + task javadocJar(type: Jar, dependsOn: javadoc) { + classifier = 'javadoc' + from javadoc.destinationDir + } + + // 打包包含kotlin源码的javadoc + task kotlinDocJar(type: Jar, dependsOn: dokkaHtml) { + classifier = 'javadoc' + from dokkaHtml.outputDirectory + } + // + + afterEvaluate { + + publishing { + publications { + mavenAndroid(MavenPublication) { + from components.release + + groupId "$groupIdDefined" + artifactId "$artifactIdDefined" + version "$versionDefined" + // 上传source + artifact sourcesJar + // 上传javadoc + if (project.plugins.hasPlugin('kotlin-android')) { + artifact kotlinDocJar + } else { + artifact javadocJar + } + + pom { + name = 'Android-SerialPort-API' + description = 'Android-SerialPort-API' + url = "$gitUrl" + + //licenses { + // license { + // name = 'The MIT License' + // url = 'https://opensource.org/licenses/MIT' + // } + //} + + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'http://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + + developers { + developer { + id = 'licheedev' + name = 'John Lee' + email = 'licheedev@foxmail.com' + } + } + scm { + connection = "$gitUrl" + developerConnection = "${gitUrl}.git" + url = "$gitUrl" + } + } + } + } + + repositories { + + maven { + // 依赖发布地址 + def releasesRepoUrl = "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + def snapshotsRepoUrl = "https://s01.oss.sonatype.org/content/repositories/snapshots/" + url = version.endsWith('SNAPSHOT') ? snapshotsRepoUrl : releasesRepoUrl + + // 配置账号密码 + println "user=${ossrhUsername},key=${ossrhPassword}" + credentials { + username "${ossrhUsername}" + password "${ossrhPassword}" + } + } + } + } + + signing { + sign publishing.publications.mavenAndroid + } + } +} \ No newline at end of file diff --git a/sample/build.gradle b/sample/build.gradle index 85a2529..00bc09a 100644 --- a/sample/build.gradle +++ b/sample/build.gradle @@ -10,7 +10,7 @@ android { versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -22,10 +22,11 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { + androidTestImplementation('androidx.test.espresso:espresso-core:3.1.0', { exclude group: 'com.android.support', module: 'support-annotations' }) - implementation 'com.android.support:appcompat-v7:27.1.1' - testImplementation 'junit:junit:4.12' - implementation project(':serialport') + implementation 'androidx.appcompat:appcompat:1.2.0' + testImplementation 'junit:junit:4.13' + //implementation project(':serialport') + implementation 'com.licheedev:android-serialport:2.1.5' } diff --git a/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java b/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java index b65e8d8..a7501ea 100644 --- a/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java +++ b/sample/src/androidTest/java/android/serialport/sample/ExampleInstrumentedTest.java @@ -1,8 +1,8 @@ package android.serialport.sample; import android.content.Context; -import android.support.test.InstrumentationRegistry; -import android.support.test.runner.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.ext.junit.runners.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/serialport/.gitignore b/serialport/.gitignore index a222170..968c49e 100644 --- a/serialport/.gitignore +++ b/serialport/.gitignore @@ -1,2 +1,3 @@ /build .externalNativeBuild +.cxx diff --git a/serialport/CMakeLists.txt b/serialport/CMakeLists.txt index f16cb32..80def00 100644 --- a/serialport/CMakeLists.txt +++ b/serialport/CMakeLists.txt @@ -2,7 +2,7 @@ # This ensures that a certain set of CMake features is available to # your build. -cmake_minimum_required(VERSION 3.4.1) +cmake_minimum_required(VERSION 3.18.1) # Specifies a library name, specifies whether the library is STATIC or # SHARED, and provides relative paths to the source code. You can @@ -35,4 +35,10 @@ target_link_libraries( # Specifies the target library. # Links the target library to the log library # included in the NDK. - ${log-lib} ) \ No newline at end of file + ${log-lib} ) + +target_link_options( + serial_port + PRIVATE + "-Wl,-z,max-page-size=16384" +) \ No newline at end of file diff --git a/serialport/build.gradle b/serialport/build.gradle index 7f171a1..b5a36b4 100644 --- a/serialport/build.gradle +++ b/serialport/build.gradle @@ -7,10 +7,10 @@ android { defaultConfig { minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode rootProject.ext.versionCode + versionCode 1 versionName rootProject.ext.versionName - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' } buildTypes { release { @@ -21,6 +21,12 @@ android { externalNativeBuild { cmake { path 'CMakeLists.txt' + version "3.18.1" + } + } + packagingOptions { + jniLibs { + useLegacyPackaging true } } } @@ -32,7 +38,10 @@ dependencies { // }) // compile 'com.android.support:appcompat-v7:25.3.0' // testCompile 'junit:junit:4.12' + + + api "androidx.annotation:annotation:1.1.0" } -apply from: '../jitpack.gradle' +apply from: '../maven_publish.gradle' diff --git a/serialport/src/main/AndroidManifest.xml b/serialport/src/main/AndroidManifest.xml index 808f2ee..6c1ed51 100644 --- a/serialport/src/main/AndroidManifest.xml +++ b/serialport/src/main/AndroidManifest.xml @@ -1,10 +1,4 @@ - - - - - - + + diff --git a/serialport/src/main/java/android/serialport/SerialPort.java b/serialport/src/main/java/android/serialport/SerialPort.java index 7633b3c..a07b705 100644 --- a/serialport/src/main/java/android/serialport/SerialPort.java +++ b/serialport/src/main/java/android/serialport/SerialPort.java @@ -17,6 +17,8 @@ package android.serialport; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; @@ -32,13 +34,19 @@ public final class SerialPort { public static final String DEFAULT_SU_PATH = "/system/bin/su"; private static String sSuPath = DEFAULT_SU_PATH; + private File device; + private int baudrate; + private int dataBits; + private int parity; + private int stopBits; + private int flags; /** * Set the su binary path, the default su binary path is {@link #DEFAULT_SU_PATH} * * @param suPath su binary path */ - public static void setSuPath(String suPath) { + public static void setSuPath(@Nullable String suPath) { if (suPath == null) { return; } @@ -50,6 +58,7 @@ public static void setSuPath(String suPath) { * * @return */ + @NonNull public static String getSuPath() { return sSuPath; } @@ -66,15 +75,28 @@ public static String getSuPath() { * * @param device 串口设备文件 * @param baudrate 波特率 - * @param dataBits 数据位,默认8 - * @param parity 奇偶校验位,默认0(无校验) - * @param stopBits 停止位,默认1 + * @param dataBits 数据位;默认8,可选值为5~8 + * @param parity 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) + * @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位 * @param flags 默认0 * @throws SecurityException * @throws IOException */ - private SerialPort(File device, int baudrate, int dataBits, int parity, int stopBits, int flags) - throws SecurityException, IOException { + public SerialPort( + @NonNull File device, int baudrate, int dataBits, int parity, int stopBits, + int flags + ) throws SecurityException, IOException { + + this.device = device; + this.baudrate = baudrate; + this.dataBits = dataBits; + this.parity = parity; + this.stopBits = stopBits; + this.flags = flags; + + if (!device.exists()) { + throw new IOException("SerialPort(" + device.getAbsolutePath() + ") not exists"); + } /* Check access permission */ if (!device.canRead() || !device.canWrite()) { @@ -96,27 +118,112 @@ private SerialPort(File device, int baudrate, int dataBits, int parity, int stop mFd = open(device.getAbsolutePath(), baudrate, dataBits, parity, stopBits, flags); if (mFd == null) { Log.e(TAG, "native open returns null"); - throw new IOException(); + throw new IOException("native open" + + "SerialPort(" + device.getAbsolutePath() + + ") returns null"); } mFileInputStream = new FileInputStream(mFd); mFileOutputStream = new FileOutputStream(mFd); } + /** + * 串口,默认的8n1 + * + * @param device 串口设备文件 + * @param baudrate 波特率 + * @throws SecurityException + * @throws IOException + */ + public SerialPort(@NonNull File device, int baudrate) throws SecurityException, IOException { + this(device, baudrate, 8, 0, 1, 0); + } + + /** + * 串口 + * + * @param device 串口设备文件 + * @param baudrate 波特率 + * @param dataBits 数据位;默认8,可选值为5~8 + * @param parity 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) + * @param stopBits 停止位;默认1;1:1位停止位;2:2位停止位 + * @throws SecurityException + * @throws IOException + */ + public SerialPort(@NonNull File device, int baudrate, int dataBits, int parity, int stopBits) + throws SecurityException, IOException { + this(device, baudrate, dataBits, parity, stopBits, 0); + } + // Getters and setters + @NonNull public InputStream getInputStream() { return mFileInputStream; } + @NonNull public OutputStream getOutputStream() { return mFileOutputStream; } + /** 串口设备文件 */ + @NonNull + public File getDevice() { + return device; + } + + /** 波特率 */ + public int getBaudrate() { + return baudrate; + } + + /** 数据位;默认8,可选值为5~8 */ + public int getDataBits() { + return dataBits; + } + + /** 奇偶校验;0:无校验位(NONE,默认);1:奇校验位(ODD);2:偶校验位(EVEN) */ + public int getParity() { + return parity; + } + + /** 停止位;默认1;1:1位停止位;2:2位停止位 */ + public int getStopBits() { + return stopBits; + } + + public int getFlags() { + return flags; + } + // JNI - private native FileDescriptor open(String absolutePath, int baudrate, int dataBits, int parity, - int stopBits, int flags); + private native FileDescriptor open( + String absolutePath, int baudrate, int dataBits, int parity, + int stopBits, int flags + ); public native void close(); + /** 关闭流和串口,已经try-catch */ + public void tryClose() { + try { + mFileInputStream.close(); + } catch (IOException e) { + //e.printStackTrace(); + } + + try { + mFileOutputStream.close(); + } catch (IOException e) { + //e.printStackTrace(); + } + + try { + close(); + } catch (Exception e) { + //e.printStackTrace(); + } + } + static { System.loadLibrary("serial_port"); }