diff --git a/build.gradle b/build.gradle index 02cd8ed..94368b0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,6 +1,4 @@ - -// Top-level build file where you can add configuration options common to all sub-projects/modules. - +apply from: "${rootProject.rootDir}/buildCommon/commonLibConfig.gradle" buildscript { repositories { google() @@ -8,7 +6,7 @@ buildscript { } dependencies { classpath 'com.android.tools.build:gradle:7.1.2' - + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.20" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } diff --git a/buildCommon/commonLibConfig.gradle b/buildCommon/commonLibConfig.gradle new file mode 100644 index 0000000..0c66e38 --- /dev/null +++ b/buildCommon/commonLibConfig.gradle @@ -0,0 +1,159 @@ +project.ext { + sign = [ + keystore_path : "../signature/innovationapp.jks", + keystore_pwd : "123456", + keystore_alias: "yinuo" + ] + + compileOptions = [ + sourceCompatibility: JavaVersion.VERSION_1_8, + targetCompatibility: JavaVersion.VERSION_1_8, + ] + + versions = [ + compileSdkVersion : 31, + buildToolsVersion : "33.0.3", + minSdkVersion : 23, + targetSdkVersion : 28, + versionCode : 1, + versionName : "1.0", + jvmTarget : "1.8", + + appcompat : "1.2.0", + material : "1.2.1", + okhttp : "4.9.1", + retrofit : "2.9.0", + rxjava : "3.0.13", + rxandroid : "3.0.0", + kotlin : "1.5.10", + kotlin_android : "1.5.0", + converter_gson : '2.9.0', + retrofit_rxjava : "2.9.0", + room : "2.3.0", + jxl : "2.6.12", + navigation_fragment_ktx: "2.3.0", + navigation_ui_ktx : "2.3.0", + glide : "4.12.0", + photo_view : "2.3.0", + luban : "1.1.8", + gson : "2.8.6", + arouter : "1.5.2", + mmkv : "1.2.10", + okhttp_logger : "3.12.7", + lifecycle_process : "2.3.1", + ] + + dependencies = [ + okhttp : "com.squareup.okhttp3:okhttp:${versions.okhttp}", + retrofit : "com.squareup.retrofit2:retrofit:${versions.retrofit}", + rxjava : "io.reactivex.rxjava3:rxjava:${versions.rxjava}", + rxandroid : "io.reactivex.rxjava3:rxandroid:${versions.rxandroid}", + okhttp_logger : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp_logger}", + kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:${versions.kotlin}", + kotlin_android : "org.jetbrains.kotlinx:kotlinx-coroutines-android:${versions.kotlin_android}", + converter_gson : "com.squareup.retrofit2:converter-gson:${versions.converter_gson}", + retrofit_rxjava : "com.squareup.retrofit2:adapter-rxjava3:${versions.retrofit_rxjava}", + room : "androidx.room:room-runtime:${versions.room}", + room_compiler : "androidx.room:room-compiler:${versions.room}", + room_ktx : "androidx.room:room-ktx:${versions.room}", + room_rxjava : "androidx.room:room-rxjava3:${versions.room}", + jxl : "net.sourceforge.jexcelapi:jxl:${versions.jxl}", + navigation_fragment_ktx: "androidx.navigation:navigation-fragment-ktx:${versions.navigation_fragment_ktx}", + navigation_ui_ktx : "androidx.navigation:navigation-ui-ktx:${versions.navigation_ui_ktx}", + navigation_arg : "androidx.navigation:navigation-safe-args-gradle-plugin:${versions.navigation_ui_ktx}", + glide : "com.github.bumptech.glide:glide:${versions.glide}", + gilde_integration : "com.github.bumptech.glide:okhttp3-integration:${versions.glide}", + annotationProcessor : "com.github.bumptech.glide:compiler:${versions.glide}", + photo_view : "com.github.chrisbanes:PhotoView:${versions.photo_view}", + luban : "top.zibin:Luban:${versions.luban}", + gson : "com.google.code.gson:gson:${versions.gson}", + arouter : "com.alibaba:arouter-api:${versions.arouter}", + arouter_compiler : "com.alibaba:arouter-compiler:${versions.arouter}", + mmkv : "com.tencent:mmkv-static:${versions.mmkv}", + lifecycle_process : "androidx.lifecycle:lifecycle-process:${versions.lifecycle_process}", + ] + + /** + * android project android {} default config + */ + setAndroidConfig = { + extension -> + extension.compileSdkVersion versions.compileSdkVersion + + extension.defaultConfig { + minSdkVersion versions.minSdkVersion + targetSdkVersion versions.targetSdkVersion + versionCode versions.versionCode + versionName versions.vserionName + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + } + + extension.compileOptions { + targetCompatibility = compileOptions.targetCompatibility + sourceCompatibility = compileOptions.sourceCompatibility + } + + extension.kotlinOptions { + jvmTarget = versions.jvmTarget + } + } + + /** + * android project dependencies {} default config + */ + setDependencies = { + extension -> + extension.implementation fileTree(include: ['*.jar'], dir: 'libs') + } + + /** + * android library config + */ + setLibDefaultConfig = { + extension -> + extension.apply plugin: 'com.android.library' + extension.apply plugin: 'kotlin-android' + extension.apply plugin: 'kotlin-parcelize' + extension.apply plugin: 'kotlin-kapt' + extension.description "lib" + + setAndroidConfig extension.android + setDependencies extension.dependencies + } + + /** + * android application config + */ + setAppDefaultConfig = { + extension -> + extension.apply plugin: 'com.android.application' + extension.apply plugin: 'kotlin-android' + extension.apply plugin: 'kotlin-parcelize' + extension.apply plugin: 'kotlin-kapt' + extension.description "lib" + + setAndroidConfig extension.android + setDependencies extension.dependencies + } + + /** + * android library common config + */ + setArouterConfig = { + extension -> + extension.android.defaultConfig { + javaCompileOptions { + annotationProcessorOptions { + arguments = [AROUTER_MODULE_NAME: project.getName()] + } + } + } + + // 添加ARouter依赖 + extension.dependencies.implementation(rootProject.ext.dependencies.arouter) { + exclude group: 'com.android.support' + } + extension.dependencies.annotationProcessor rootProject.ext.dependencies.arouter_compiler + } +} \ No newline at end of file diff --git a/commonSPort/.gitignore b/commonSPort/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/commonSPort/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/commonSPort/build.gradle b/commonSPort/build.gradle new file mode 100644 index 0000000..c3321be --- /dev/null +++ b/commonSPort/build.gradle @@ -0,0 +1,22 @@ +apply from: "${rootProject.rootDir}/buildCommon/commonLibConfig.gradle" +project.ext.setLibDefaultConfig project + +android { + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildFeatures { + viewBinding true + } +} + +dependencies { + implementation 'com.aill:AndroidSerialPort:1.0.8' +} \ No newline at end of file diff --git a/commonSPort/consumer-rules.pro b/commonSPort/consumer-rules.pro new file mode 100644 index 0000000..e69de29 diff --git a/commonSPort/proguard-rules.pro b/commonSPort/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/commonSPort/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/commonSPort/src/main/AndroidManifest.xml b/commonSPort/src/main/AndroidManifest.xml new file mode 100644 index 0000000..4d8db79 --- /dev/null +++ b/commonSPort/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/queue/BlockTaskQueue.java b/commonSPort/src/main/java/com/common/queue/BlockTaskQueue.java new file mode 100644 index 0000000..86d9fac --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/BlockTaskQueue.java @@ -0,0 +1,68 @@ +package com.common.queue; + +import android.util.Log; + +import com.common.queue.Task.ITask; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; +import java.util.concurrent.atomic.AtomicInteger; + +public class BlockTaskQueue { + private String TAG = "BlockTaskQueue"; + private AtomicInteger mAtomicInteger = new AtomicInteger(); + //阻塞队列 + private final BlockingQueue mTaskQueue = new PriorityBlockingQueue<>(); + + public BlockTaskQueue() { + } + + //单例模式 +// private static class BlockTaskQueueHolder { +// private final static BlockTaskQueue INSTANCE = new BlockTaskQueue(); +// } +// +// public static BlockTaskQueue getInstance() { +// return BlockTaskQueueHolder.INSTANCE; +// } + + /** + * 插入时 因为每一个Task都实现了comparable接口 所以队列会按照Task复写的compare()方法定义的优先级次序进行插入 + * 当优先级相同时,使用AtomicInteger原子类自增 来为每一个task 设置sequence, + * sequence的作用是标记两个相同优先级的任务入队的次序 + */ + public int add(T task) { + if (!mTaskQueue.contains(task)) { + task.setSequence(mAtomicInteger.incrementAndGet()); + mTaskQueue.add(task); + Log.d(TAG, "\n add task " + task.toString()); + } + return mTaskQueue.size(); + } + + public void remove(T task) { + if (mTaskQueue.contains(task)) { + Log.d(TAG, "\n" + "task has been finished. remove it from task queue"); + mTaskQueue.remove(task); + } + if (mTaskQueue.size() == 0) { + mAtomicInteger.set(0); + } + } + + public ITask poll() { + return mTaskQueue.poll(); + } + + public ITask take() throws InterruptedException { + return mTaskQueue.take(); + } + + public void clear() { + mTaskQueue.clear(); + } + + public int size() { + return mTaskQueue.size(); + } +} diff --git a/commonSPort/src/main/java/com/common/queue/Task/BaseTask.java b/commonSPort/src/main/java/com/common/queue/Task/BaseTask.java new file mode 100644 index 0000000..8b69f84 --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/Task/BaseTask.java @@ -0,0 +1,108 @@ +package com.common.queue.Task; + +import android.util.Log; + +import com.common.queue.TaskScheduler; +import com.common.queue.BlockTaskQueue; + +import java.lang.ref.WeakReference; +import java.util.concurrent.PriorityBlockingQueue; + +public class BaseTask implements ITask { + private final String TAG = getClass().getSimpleName(); + private TaskPriority mTaskPriority = TaskPriority.DEFAULT; //默认优先级 + private int mSequence;// 入队次序 + private Boolean mTaskStatus = false; // 标志任务状态,是否仍在展示 + protected WeakReference taskQueue;//阻塞队列 + protected int duration = 0; //任务执行时间 + //此队列用来实现任务时间不确定的队列阻塞功能 + private PriorityBlockingQueue blockQueue; + //构造函数 + public BaseTask() { + taskQueue = new WeakReference<>(BlockTaskQueue.getInstance()); + blockQueue = new PriorityBlockingQueue<>(); + } + //入队实现 + @Override + public void enqueue() { + TaskScheduler.getInstance().enqueue(this); + } + //执行任务方法,此时标记为设为true,并且将当前任务记录下来 + @Override + public void doTask() { + mTaskStatus = true; + } + //任务执行完成,改变标记位,将任务在队列中移除,并且把记录清除 + @Override + public void finishTask() { + this.mTaskStatus = false; + this.taskQueue.get().remove(this); + Log.d(TAG, taskQueue.get().size() + ""); + } + //设置任务优先级实现 + @Override + public ITask setPriority(TaskPriority mTaskPriority) { + this.mTaskPriority = mTaskPriority; + return this; + } + //设置任务执行时间 + public ITask setDuration(int duration) { + this.duration = duration; + return this; + } + //获取任务优先级 + @Override + public TaskPriority getPriority() { + return mTaskPriority; + } + //获取任务执行时间 + @Override + public int getDuration() { + return duration; + } + //设置任务次序 + @Override + public void setSequence(int mSequence) { + this.mSequence = mSequence; + } + //获取任务次序 + @Override + public int getSequence() { + return mSequence; + } + // 获取任务状态 + @Override + public boolean getStatus() { + return mTaskStatus; + } + //阻塞任务执行 + @Override + public void blockTask() throws Exception { + blockQueue.take(); //如果队列里面没数据,就会一直阻塞 + } + //解除阻塞 + @Override + public void unLockBlock() { + blockQueue.add(1); //往里面随便添加一个数据,阻塞就会解除 + } + + /** + * 排队实现 + * 优先级的标准如下: + * TaskPriority.LOW < TaskPriority.DEFAULT < TaskPriority.HIGH + * 当优先级相同 按照插入次序排队 + */ + @Override + public int compareTo(ITask another) { + final TaskPriority me = this.getPriority(); + final TaskPriority it = another.getPriority(); + return me == it ? this.getSequence() - another.getSequence() : + it.ordinal() - me.ordinal(); + } + //输出一些信息 + @Override + public String toString() { + return "task name : " + getClass().getSimpleName() + " sequence : " + mSequence + " TaskPriority : " + mTaskPriority; + } +} + \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/queue/Task/DemoTask.java b/commonSPort/src/main/java/com/common/queue/Task/DemoTask.java new file mode 100644 index 0000000..e972614 --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/Task/DemoTask.java @@ -0,0 +1,51 @@ +package com.common.queue.Task; + +import android.util.Log; + +public class DemoTask extends BaseTask { + String name; + + public DemoTask(String name) { + this.name = name; + } + + //执行任务方法,在这里实现你的任务具体内容 + @Override + public void doTask() { + super.doTask(); + Log.i("LogTask", "--doTask-" + name); + + //如果这个Task的执行时间是不确定的,比如上传图片,那么在上传成功后需要手动调用 + //unLockBlock方法解除阻塞,例如: + uploadImage(new UploadListener() { + @Override + public void onSuccess() { + unLockBlock(); + } + }); + } + + private void uploadImage(UploadListener uploadListener) { + try { +// long seconds = getDuration(); + long seconds = (long) (Math.random() * 10000); + Log.i("LogTask", "uploadImage milliseconds " + seconds); + Thread.sleep(seconds); + } catch (Exception e) { + e.printStackTrace(); + } + uploadListener.onSuccess(); + } + + //任务执行完的回调,在这里你可以做些释放资源或者埋点之类的操作 + @Override + public void finishTask() { + super.finishTask(); + Log.i("LogTask", "--finishTask-" + name); + } + + public interface UploadListener { + void onSuccess(); + } + +} diff --git a/commonSPort/src/main/java/com/common/queue/Task/ITask.java b/commonSPort/src/main/java/com/common/queue/Task/ITask.java new file mode 100644 index 0000000..458f798 --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/Task/ITask.java @@ -0,0 +1,41 @@ +package com.common.queue.Task; + +public interface ITask extends Comparable { + + // 将该任务插入队列 + void enqueue(); + + // 执行具体任务的方法 + void doTask(); + + // 任务执行完成后的回调方法 + void finishTask(); + + // 设置任务优先级 + ITask setPriority(TaskPriority mTaskPriority); + + // 获取任务优先级 + TaskPriority getPriority(); + + // 当优先级相同 按照插入顺序 先入先出 该方法用来标记插入顺序 + void setSequence(int mSequence); + + // 获取入队次序 + int getSequence(); + + // 每个任务的状态,就是标记完成和未完成 + boolean getStatus(); + + // 设置每个任务的执行时间,该方法用于任务执行时间确定的情况 + ITask setDuration(int duration); + + // 获取每个任务执行的时间 + int getDuration(); + + // 阻塞任务执行,该方法用于任务执行时间不确定的情况 + void blockTask() throws Exception; + + // 解除阻塞任务,该方法用于任务执行时间不确定的情况 + void unLockBlock(); +} + diff --git a/commonSPort/src/main/java/com/common/queue/Task/TaskEvent.java b/commonSPort/src/main/java/com/common/queue/Task/TaskEvent.java new file mode 100644 index 0000000..356fb30 --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/Task/TaskEvent.java @@ -0,0 +1,30 @@ +package com.common.queue.Task; + +import java.lang.ref.WeakReference; + +public class TaskEvent { + private WeakReference mTask; + int mEventType; + + public ITask getTask() { + return mTask.get(); + } + + public void setTask(ITask mTask) { + this.mTask = new WeakReference<>(mTask); + } + + public int getEventType() { + return mEventType; + } + + public void setEventType(int mEventType) { + this.mEventType = mEventType; + } + + public static class EventType { + public static final int DO = 0X00; + public static final int FINISH = 0X01; + } +} + \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/queue/Task/TaskPriority.java b/commonSPort/src/main/java/com/common/queue/Task/TaskPriority.java new file mode 100644 index 0000000..d435b95 --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/Task/TaskPriority.java @@ -0,0 +1,8 @@ +package com.common.queue.Task; + +// 优先级分为3种,如注释所示,他们的关系:LOW < DEFAULT < HIGH +public enum TaskPriority { + LOW, //低 + DEFAULT,//普通 + HIGH, //高 +} diff --git a/commonSPort/src/main/java/com/common/queue/TaskExecutor.java b/commonSPort/src/main/java/com/common/queue/TaskExecutor.java new file mode 100644 index 0000000..a0f73bd --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/TaskExecutor.java @@ -0,0 +1,100 @@ +package com.common.queue; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; + +import com.common.queue.Task.TaskEvent; +import com.common.queue.Task.ITask; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +public class TaskExecutor { + private final String TAG = "ShowTaskExecutor"; + private BlockTaskQueue taskQueue; + private TaskHandler mTaskHandler; + private boolean isRunning = true; + private static final int MSG_EVENT_DO = 0; + private static final int MSG_EVENT_FINISH = 1; + + public TaskExecutor(BlockTaskQueue taskQueue) { + this.taskQueue = taskQueue; + mTaskHandler = new TaskHandler(); + } + //开始遍历任务队列 + public void start() { + Executors.newSingleThreadExecutor().execute(new Runnable() { + @Override + public void run() { + try { + while (isRunning) { //死循环 + ITask iTask; + iTask = taskQueue.take(); //取任务 + if (iTask != null) { + //执行任务 + TaskEvent doEvent = new TaskEvent(); + doEvent.setTask(iTask); + doEvent.setEventType(TaskEvent.EventType.DO); + mTaskHandler.obtainMessage(MSG_EVENT_DO, doEvent).sendToTarget(); + //一直阻塞,直到任务执行完 + if (iTask.getDuration()!=0) { + TimeUnit.MILLISECONDS.sleep(iTask.getDuration()); + }else { + iTask.blockTask(); + } + //完成任务 + TaskEvent finishEvent = new TaskEvent(); + finishEvent.setTask(iTask); + finishEvent.setEventType(TaskEvent.EventType.FINISH); + mTaskHandler.obtainMessage(MSG_EVENT_FINISH, finishEvent).sendToTarget(); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + } + } + }); + } + //根据不同的消息回调不同的方法。 + private static class TaskHandler extends Handler { + TaskHandler() { + super(Looper.getMainLooper()); + } + + @Override + public void handleMessage(Message msg) { + super.handleMessage(msg); + TaskEvent taskEvent = (TaskEvent) msg.obj; + if (msg.what == MSG_EVENT_DO && taskEvent.getEventType() == TaskEvent.EventType.DO) { + taskEvent.getTask().doTask(); + } + if (msg.what == MSG_EVENT_FINISH && taskEvent.getEventType() == TaskEvent.EventType.FINISH) { + taskEvent.getTask().finishTask(); + } + } + } + + public void startRunning() { + isRunning = true; + } + + public void pauseRunning() { + isRunning = false; + } + + public boolean isRunning() { + return isRunning; + } + + public void resetExecutor() { + isRunning = true; + taskQueue.clear(); + } + + public void clearExecutor() { + pauseRunning(); + taskQueue.clear(); + } +} + \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/queue/TaskScheduler.java b/commonSPort/src/main/java/com/common/queue/TaskScheduler.java new file mode 100644 index 0000000..7b04d2a --- /dev/null +++ b/commonSPort/src/main/java/com/common/queue/TaskScheduler.java @@ -0,0 +1,44 @@ +package com.common.queue; + +import com.common.queue.Task.ITask; + +public class TaskScheduler { + private final String TAG = "TaskScheduler"; + private BlockTaskQueue mTaskQueue = new BlockTaskQueue(); + private TaskExecutor mExecutor; + +// private static class ShowDurationHolder { +// private final static TaskScheduler INSTANCE = new TaskScheduler(); +// } + + public TaskScheduler() { + initExecutor(); + } + + private void initExecutor() { + mExecutor = new TaskExecutor(mTaskQueue); + mExecutor.start(); + } + +// public static TaskScheduler getInstance() { +// return ShowDurationHolder.INSTANCE; +// } + + public void enqueue(ITask task) { + //因为TaskScheduler这里写成单例,如果isRunning改成false的话,不判断一下,就会一直都是false + if (!mExecutor.isRunning()) { + mExecutor.startRunning(); + } + //按照优先级插入队列 依次播放 + mTaskQueue.add(task); + } + + public void resetExecutor() { + mExecutor.resetExecutor(); + } + + public void clearExecutor() { + mExecutor.clearExecutor(); + } +} + \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/serialport/EasySerialPort.kt b/commonSPort/src/main/java/com/common/serialport/EasySerialPort.kt new file mode 100644 index 0000000..ccb1fbc --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/EasySerialPort.kt @@ -0,0 +1,103 @@ +package com.common.serialport + +import android.util.Log +import com.aill.androidserialport.SerialPort +import com.common.serialport.inter.SerialPortHelper +import com.common.queue.TaskScheduler +import java.io.IOException + +class EasySerialPort( + private val portPath: String, + private val baudRate: Int, + private val mReceiver: (ByteArray) -> Unit +) : Runnable { + private var mPort: SerialPort? = null + private var startReceiveMsg = true + private var autoRetryConnect = false + + init { + openSerialPort(); + } + + private fun openSerialPort() { + mPort = SerialPortHelper.openSerialPort(portPath, baudRate) + mPort?.let { + startReceiveMsg = true + Thread(this).start() + } + } + + /** + * 关闭串口 + */ + open fun closePort() { + startReceiveMsg = false + SerialPortHelper.closePort(mPort) + } + + /** + * 循环读消息 + */ + override fun run() { + while (startReceiveMsg) { + try { + val ips = mPort?.inputStream + val readByte = ips?.available()?.let { ByteArray(it) } + readByte?.let { + val size = ips.read(it) + if (size > 0) { + mReceiver.invoke(readByte) + } + } + } catch (e: IOException) { + Log.e( + "EasySerialPort", + "read msg error; path = " + portPath + ", error msg = " + e.message + ) + e.printStackTrace() + if (autoRetryConnect) { + closePort() + openSerialPort() + } + } + } + } + + /** + * 写消息 + */ + open fun write(data: ByteArray) { + val task = SendMsgTask(data) { + try { + val outputStream = mPort?.outputStream + outputStream?.write(it) + outputStream?.flush() + } catch (e: IOException) { + Log.e( + "EasySerialPort", + "write msg error; path = " + portPath + ", error msg = " + e.message + ) + e.printStackTrace() + if (autoRetryConnect) { + closePort() + openSerialPort() + } + } + } + TaskScheduler.getInstance().enqueue(task) + } + + /** + * 开始自动重连 + */ + open fun enableAutoConnect() { + this.autoRetryConnect = true + } + + /** + * 关闭自动重连 + */ + open fun disableAutoConnect() { + this.autoRetryConnect = true + } +} \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/serialport/SendMsgTask.kt b/commonSPort/src/main/java/com/common/serialport/SendMsgTask.kt new file mode 100644 index 0000000..f3392db --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/SendMsgTask.kt @@ -0,0 +1,14 @@ +package com.common.serialport + +import com.common.queue.Task.BaseTask + +class SendMsgTask(private val byteArray: ByteArray, private val action: (ByteArray) -> Unit) : + BaseTask() { + + override fun doTask() { + super.doTask() + action.invoke(byteArray) + unLockBlock() + } + +} \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/serialport/inter/IHelper.kt b/commonSPort/src/main/java/com/common/serialport/inter/IHelper.kt new file mode 100644 index 0000000..8f0fbea --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/inter/IHelper.kt @@ -0,0 +1,10 @@ +package com.common.serialport.inter + +import com.aill.androidserialport.SerialPort + +interface IHelper { + + fun openSerialPort(portPath: String, baudrate: Int): SerialPort?; + + fun closePort(port: SerialPort?); +} \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/serialport/inter/SerialPortHelper.kt b/commonSPort/src/main/java/com/common/serialport/inter/SerialPortHelper.kt new file mode 100644 index 0000000..8cecf6a --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/inter/SerialPortHelper.kt @@ -0,0 +1,29 @@ +package com.common.serialport.inter + +import android.util.Log +import com.aill.androidserialport.SerialPort +import java.io.File +import java.io.IOException + +object SerialPortHelper : IHelper { + override fun openSerialPort(portPath: String, baudrate: Int): SerialPort? { + try { + return SerialPort(File(portPath), baudrate, 0); + } catch (e: IOException) { + e.printStackTrace() + Log.e("SerialPortHelper", "open port error; " + e.message) + } + return null; + } + + override fun closePort(port: SerialPort?) { + try { + port?.close() + port?.inputStream?.close() + port?.outputStream?.close() + } catch (e: Exception) { + e.printStackTrace() + Log.e("SerialPortHelper", "closePort error; " + e.message) + } + } +} \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/serialport/utils/CommonPortUtils.kt b/commonSPort/src/main/java/com/common/serialport/utils/CommonPortUtils.kt new file mode 100644 index 0000000..1128523 --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/utils/CommonPortUtils.kt @@ -0,0 +1,70 @@ +package com.common.serialport.utils + +import com.common.serialport.EasySerialPort + +/** + * @author xiaowusky + * 操作串口工具类 + * 可以同时处理多个串口操作 + */ +object CommonPortUtils { + + private val portMaps = HashMap() + + /** + * 打开串口 + * @param portPath 串口地址 + * @param baudRate 波特率 + * @param mReceiver 接收消息回调 + */ + fun openPort( + portPath: String, + baudRate: Int, + mReceiver: (ByteArray) -> Unit + ) { + if (portMaps.containsKey(portPath)) { + return + } + val easyPort = EasySerialPort(portPath, baudRate, mReceiver) + portMaps.put(portPath, easyPort) + } + + /** + * 发送消息 + * @param portPath 串口地址 + * @param data 消息byte数组 + */ + fun sendMsg(portPath: String, data: ByteArray) { + SinglePortUtils.easyPort?.write(data) + } + + /** + * 发送消息 + * @param portPath 串口地址 + * @param data String类型消息,内部会toByteArray + */ + fun sendMsg(portPath: String, data: String) { + SinglePortUtils.easyPort?.write(data.toByteArray()) + } + + /** + * 释放某个串口 + * @param portPath 串口地址 + */ + fun release(portPath: String) { + if (portMaps.containsKey(portPath)) { + portMaps.get(portPath)?.closePort() + } + portMaps.remove(portPath) + } + + /** + * 释放所有串口 + */ + fun releaseAll() { + portMaps.forEach { + it.value?.closePort() + } + portMaps.clear() + } +} \ No newline at end of file diff --git a/commonSPort/src/main/java/com/common/serialport/utils/DataUtils.java b/commonSPort/src/main/java/com/common/serialport/utils/DataUtils.java new file mode 100644 index 0000000..a5d45bd --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/utils/DataUtils.java @@ -0,0 +1,139 @@ +package com.common.serialport.utils; + +import java.util.ArrayList; +import java.util.List; + +/** + * 串口数据转换工具类 + * Created by Administrator on 2016/6/2. + */ +public class DataUtils { + //------------------------------------------------------- + // 判断奇数或偶数,位运算,最后一位是1则为奇数,为0是偶数 + public static int isOdd(int num) { + return num & 1; + } + + //------------------------------------------------------- + //Hex字符串转int + public static int HexToInt(String inHex) { + return Integer.parseInt(inHex, 16); + } + + public static String IntToHex(int intHex){ + return Integer.toHexString(intHex); + } + + //------------------------------------------------------- + //Hex字符串转byte + public static byte HexToByte(String inHex) { + return (byte) Integer.parseInt(inHex, 16); + } + + //------------------------------------------------------- + //1字节转2个Hex字符 + public static String Byte2Hex(Byte inByte) { + return String.format("%02x", new Object[]{inByte}).toUpperCase(); + } + + //------------------------------------------------------- + //字节数组转转hex字符串 + public static String ByteArrToHex(byte[] inBytArr) { + StringBuilder strBuilder = new StringBuilder(); + for (byte valueOf : inBytArr) { + strBuilder.append(Byte2Hex(Byte.valueOf(valueOf))); + strBuilder.append(" "); + } + return strBuilder.toString(); + } + + //------------------------------------------------------- + //字节数组转转hex字符串,可选长度 + public static String ByteArrToHex(byte[] inBytArr, int offset, int byteCount) { + StringBuilder strBuilder = new StringBuilder(); + int j = byteCount; + for (int i = offset; i < j; i++) { + strBuilder.append(Byte2Hex(Byte.valueOf(inBytArr[i]))); + } + return strBuilder.toString(); + } + + //------------------------------------------------------- + //转hex字符串转字节数组 + public static byte[] HexToByteArr(String inHex) { + byte[] result; + int hexlen = inHex.length(); + if (isOdd(hexlen) == 1) { + hexlen++; + result = new byte[(hexlen / 2)]; + inHex = "0" + inHex; + } else { + result = new byte[(hexlen / 2)]; + } + int j = 0; + for (int i = 0; i < hexlen; i += 2) { + result[j] = HexToByte(inHex.substring(i, i + 2)); + j++; + } + return result; + } + + /** + * 按照指定长度切割字符串 + * + * @param inputString 需要切割的源字符串 + * @param length 指定的长度 + * @return + */ + public static List getDivLines(String inputString, int length) { + List divList = new ArrayList<>(); + int remainder = (inputString.length()) % length; + // 一共要分割成几段 + int number = (int) Math.floor((inputString.length()) / length); + for (int index = 0; index < number; index++) { + String childStr = inputString.substring(index * length, (index + 1) * length); + divList.add(childStr); + } + if (remainder > 0) { + String cStr = inputString.substring(number * length, inputString.length()); + divList.add(cStr); + } + return divList; + } + + /** + * 计算长度,两个字节长度 + * + * @param val value + * @return 结果 + */ + public static String twoByte(String val) { + if (val.length() > 4) { + val = val.substring(0, 4); + } else { + int l = 4 - val.length(); + for (int i = 0; i < l; i++) { + val = "0" + val; + } + } + return val; + } + + /** + * 校验和 + * + * @param cmd 指令 + * @return 结果 + */ + public static String sum(String cmd) { + List cmdList = DataUtils.getDivLines(cmd, 2); + int sumInt = 0; + for (String c : cmdList) { + sumInt += DataUtils.HexToInt(c); + } + String sum = DataUtils.IntToHex(sumInt); + sum = DataUtils.twoByte(sum); + cmd += sum; + return cmd.toUpperCase(); + } +} diff --git a/commonSPort/src/main/java/com/common/serialport/utils/SinglePortUtils.kt b/commonSPort/src/main/java/com/common/serialport/utils/SinglePortUtils.kt new file mode 100644 index 0000000..7d06f16 --- /dev/null +++ b/commonSPort/src/main/java/com/common/serialport/utils/SinglePortUtils.kt @@ -0,0 +1,51 @@ + +package com.common.serialport.utils + +import com.common.serialport.EasySerialPort + +/** + * @author xiaowusky + * 操作单个串口工具类 + */ +object SinglePortUtils { + var easyPort: EasySerialPort? = null + + /** + * 初始化参数 + * @param portPath 串口地址 + * @param baudRate 波特率 + * @param mReceiver 接收消息回调 + */ + fun initParams( + portPath: String, + baudRate: Int, + mReceiver: (ByteArray) -> Unit + ) { + easyPort = EasySerialPort(portPath, baudRate, mReceiver) + } + + /** + * 发送消息 + * @param portPath 串口地址 + * @param data 消息byte数组 + */ + fun sendMsg(data: ByteArray) { + easyPort?.write(data) + } + + /** + * 发送消息 + * @param portPath 串口地址 + * @param data String类型消息,内部会toByteArray + */ + fun sendMsg(data: String) { + easyPort?.write(data.toByteArray()) + } + + /** + * 释放串口 + */ + fun release(){ + easyPort?.closePort() + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 7e85a5e..4531efe 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ include ':myapplication' //, ':libuvccamera-release' include ':library' include ':ijkplayer-java' +include ':commonSPort'