├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── licheedev
│ │ └── serialtool
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── licheedev
│ │ │ └── serialtool
│ │ │ ├── App.java
│ │ │ ├── activity
│ │ │ ├── LoadCmdListActivity.java
│ │ │ ├── MainActivity.java
│ │ │ └── base
│ │ │ │ └── BaseActivity.java
│ │ │ ├── comn
│ │ │ ├── Device.java
│ │ │ ├── SerialPortManager.java
│ │ │ ├── SerialReadThread.java
│ │ │ └── message
│ │ │ │ ├── IMessage.java
│ │ │ │ ├── LogManager.java
│ │ │ │ ├── RecvMessage.java
│ │ │ │ └── SendMessage.java
│ │ │ ├── fragment
│ │ │ └── LogFragment.java
│ │ │ ├── model
│ │ │ └── Command.java
│ │ │ └── util
│ │ │ ├── AllCapTransformationMethod.java
│ │ │ ├── BaseListAdapter.java
│ │ │ ├── CommandParser.java
│ │ │ ├── ListViewHolder.java
│ │ │ ├── PrefHelper.java
│ │ │ ├── RxUtil.java
│ │ │ ├── TimeUtil.java
│ │ │ ├── ToastUtil.java
│ │ │ └── constant
│ │ │ └── PreferenceKeys.java
│ └── res
│ │ ├── color
│ │ ├── selector_log_text.xml
│ │ └── selector_spinner_text.xml
│ │ ├── drawable
│ │ ├── selector_spinner_default.xml
│ │ ├── selector_titlebar_button_bg.xml
│ │ └── shape_toast_bg.xml
│ │ ├── layout
│ │ ├── activity_load_cmd_list.xml
│ │ ├── activity_main.xml
│ │ ├── custom_toast.xml
│ │ ├── fragment_log.xml
│ │ ├── include_fragment_container.xml
│ │ ├── item_load_command_list.xml
│ │ ├── item_log.xml
│ │ ├── spinner_default_item.xml
│ │ └── spinner_item.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_title_bar_left_arrow.png
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── ic_title_bar_left_arrow.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── string_arrays.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── com
│ └── licheedev
│ └── serialtool
│ └── ExampleUnitTest.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── pics
├── 1.png
├── 2.png
└── 3.jpg
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | .svn
4 | .idea
5 | /local.properties
6 | /.idea/workspace.xml
7 | /.idea/libraries
8 | .DS_Store
9 | .idea
10 | /build
11 | /captures
12 | .externalNativeBuild
13 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Android-SerialPort-Tool
2 | Android串口调试助手
3 |
4 | 根据[**Android官方提供的串口API**](https://github.com/licheedev/Android-SerialPort-API)写的一个小工具,
5 | 没啥功能,基本也就用来调调控制板哪串口能用,发送一下简单的命令,
6 | 如果没懒癌发作的话,以后可能会加点最近命令保存啥的功能。
7 |
8 |
9 | 加载命令列表只支持这种格式
10 | ```
11 | AABBCCDDEEFF // 命令注释
12 | ```
13 |
14 | 
15 |
16 | 
17 |
18 | 
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 |
5 | compileSdkVersion rootProject.ext.compileSdkVersion
6 |
7 | defaultConfig {
8 | applicationId "com.licheedev.serialtool"
9 | minSdkVersion rootProject.ext.minSdkVersion
10 | targetSdkVersion rootProject.ext.targetSdkVersion
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
14 | }
15 |
16 | buildTypes {
17 | release {
18 | minifyEnabled false
19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
20 | }
21 | }
22 |
23 | compileOptions {
24 | sourceCompatibility 1.8
25 | targetCompatibility 1.8
26 | }
27 | }
28 |
29 | dependencies {
30 | implementation fileTree(dir: 'libs', include: ['*.jar'])
31 |
32 | androidTestCompile('androidx.test.espresso:espresso-core:3.1.0', {
33 | exclude group: 'com.android.support', module: 'support-annotations'
34 | })
35 |
36 | testImplementation 'junit:junit:4.12'
37 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
38 |
39 | implementation "androidx.appcompat:appcompat:1.2.0"
40 | //implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
41 |
42 | implementation 'com.github.licheedev.CommonSize:common_size_w1080_n1920:1.3.1'
43 |
44 | implementation 'com.jakewharton:butterknife:10.2.0'
45 | annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
46 |
47 | // 串口
48 | implementation 'com.licheedev:android-serialport:2.1.2'
49 |
50 | //rx
51 | implementation 'io.reactivex.rxjava2:rxjava:2.2.19'
52 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
53 | // eventbus
54 | implementation 'org.greenrobot:eventbus:3.2.0'
55 | // 选文件的
56 | implementation 'ru.bartwell:exfilepicker:2.1'
57 |
58 | // 硬件操作工具类
59 | implementation 'com.licheedev:hardwareutils:1.0.0'
60 | implementation 'com.licheedev:logplus:1.0.0'
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\DevTools\Android\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/licheedev/serialtool/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool;
2 |
3 | import android.content.Context;
4 | import androidx.test.platform.app.InstrumentationRegistry;
5 | import androidx.test.ext.junit.runners.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumentation test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() throws Exception {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.itdlc.coin", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licheedev/serialtool/App.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool;
2 |
3 | import android.app.Application;
4 | import android.os.Handler;
5 | import com.licheedev.serialtool.util.PrefHelper;
6 |
7 | /**
8 | * Created by Administrator on 2017/3/28 0028.
9 | */
10 |
11 | public class App extends Application {
12 |
13 | private Handler mUiHandler;
14 | private static App sInstance;
15 |
16 | @Override
17 | public void onCreate() {
18 | super.onCreate();
19 |
20 | sInstance = this;
21 | mUiHandler = new Handler();
22 | initUtils();
23 | }
24 |
25 | private void initUtils() {
26 | PrefHelper.initDefault(this);
27 | }
28 |
29 | public static App instance() {
30 | return sInstance;
31 | }
32 |
33 | public static Handler getUiHandler() {
34 | return instance().mUiHandler;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licheedev/serialtool/activity/LoadCmdListActivity.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.text.TextUtils;
6 | import android.view.View;
7 | import android.widget.AdapterView;
8 | import android.widget.Button;
9 | import android.widget.ListView;
10 | import butterknife.BindView;
11 | import butterknife.OnClick;
12 | import com.licheedev.myutils.LogPlus;
13 | import com.licheedev.serialtool.R;
14 | import com.licheedev.serialtool.activity.base.BaseActivity;
15 | import com.licheedev.serialtool.comn.SerialPortManager;
16 | import com.licheedev.serialtool.model.Command;
17 | import com.licheedev.serialtool.util.BaseListAdapter;
18 | import com.licheedev.serialtool.util.CommandParser;
19 | import com.licheedev.serialtool.util.ListViewHolder;
20 | import com.licheedev.serialtool.util.ToastUtil;
21 | import io.reactivex.Observer;
22 | import io.reactivex.disposables.Disposable;
23 | import java.io.File;
24 | import java.util.List;
25 | import ru.bartwell.exfilepicker.ExFilePicker;
26 | import ru.bartwell.exfilepicker.data.ExFilePickerResult;
27 |
28 | public class LoadCmdListActivity extends BaseActivity implements AdapterView.OnItemClickListener {
29 |
30 | public static final int REQUEST_FILE = 233;
31 | @BindView(R.id.btn_load_list)
32 | Button mBtnLoadList;
33 | @BindView(R.id.list_view)
34 | ListView mListView;
35 |
36 | private ExFilePicker mFilePicker;
37 | private CommandParser mParser;
38 | private InnerAdapter mAdapter;
39 |
40 | @Override
41 | protected int getLayoutId() {
42 | return R.layout.activity_load_cmd_list;
43 | }
44 |
45 | @Override
46 | protected void onCreate(Bundle savedInstanceState) {
47 | super.onCreate(savedInstanceState);
48 | setActionBar(true, "加载命令列表");
49 | initFilePickers();
50 |
51 | mParser = new CommandParser();
52 |
53 | mListView.setOnItemClickListener(this);
54 | mAdapter = new InnerAdapter();
55 | mListView.setAdapter(mAdapter);
56 | }
57 |
58 | @Override
59 | protected void onActivityResult(int requestCode, int resultCode, Intent data) {
60 | super.onActivityResult(requestCode, resultCode, data);
61 |
62 | if (requestCode == REQUEST_FILE) {
63 | ExFilePickerResult result = ExFilePickerResult.getFromIntent(data);
64 | if (result != null && result.getCount() > 0) {
65 | File file = new File(result.getPath(), result.getNames().get(0));
66 |
67 | mParser.rxParse(file).subscribe(new Observer>() {
68 | @Override
69 | public void onSubscribe(Disposable d) {
70 |
71 | }
72 |
73 | @Override
74 | public void onNext(List commands) {
75 | mAdapter.setNewData(commands);
76 | }
77 |
78 | @Override
79 | public void onError(Throwable e) {
80 | LogPlus.e("解析失败", e);
81 | }
82 |
83 | @Override
84 | public void onComplete() {
85 |
86 | }
87 | });
88 | } else {
89 | ToastUtil.showOne(this, "未选择文件");
90 | }
91 | }
92 | }
93 |
94 | /**
95 | * 初始化文件/文件夹选择器
96 | */
97 | private void initFilePickers() {
98 |
99 | mFilePicker = new ExFilePicker();
100 | mFilePicker.setNewFolderButtonDisabled(true);
101 | mFilePicker.setQuitButtonEnabled(true);
102 | mFilePicker.setUseFirstItemAsUpEnabled(true);
103 | mFilePicker.setCanChooseOnlyOneItem(true);
104 | mFilePicker.setShowOnlyExtensions("txt");
105 | mFilePicker.setChoiceType(ExFilePicker.ChoiceType.FILES);
106 | }
107 |
108 | @OnClick({ R.id.btn_load_list })
109 | public void onViewClicked(View view) {
110 | switch (view.getId()) {
111 | case R.id.btn_load_list:
112 | mFilePicker.start(this, REQUEST_FILE);
113 | break;
114 | }
115 | }
116 |
117 | @Override
118 | public void onItemClick(AdapterView> parent, View view, int position, long id) {
119 | Command item = mAdapter.getItem(position);
120 | SerialPortManager.instance().sendCommand(item.getCommand());
121 | }
122 |
123 | private static class InnerAdapter extends BaseListAdapter {
124 | @Override
125 | protected void inflateItem(ListViewHolder holder, int position) {
126 |
127 | Command item = getItem(position);
128 |
129 | String comment = String.valueOf(position + 1);
130 | comment =
131 | TextUtils.isEmpty(item.getComment()) ? comment : comment + " " + item.getComment();
132 |
133 | holder.setText(R.id.tv_comment, comment);
134 | holder.setText(R.id.tv_command, item.getCommand());
135 | }
136 |
137 | @Override
138 | public int getItemLayoutId(int viewType) {
139 | return R.layout.item_load_command_list;
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licheedev/serialtool/activity/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool.activity;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.serialport.SerialPortFinder;
6 | import android.text.TextUtils;
7 | import android.view.View;
8 | import android.widget.AdapterView;
9 | import android.widget.ArrayAdapter;
10 | import android.widget.Button;
11 | import android.widget.EditText;
12 | import android.widget.Spinner;
13 | import butterknife.BindView;
14 | import butterknife.OnClick;
15 | import com.licheedev.serialtool.R;
16 | import com.licheedev.serialtool.activity.base.BaseActivity;
17 | import com.licheedev.serialtool.comn.Device;
18 | import com.licheedev.serialtool.comn.SerialPortManager;
19 | import com.licheedev.serialtool.util.AllCapTransformationMethod;
20 | import com.licheedev.serialtool.util.PrefHelper;
21 | import com.licheedev.serialtool.util.ToastUtil;
22 | import com.licheedev.serialtool.util.constant.PreferenceKeys;
23 |
24 | import static com.licheedev.serialtool.R.array.baudrates;
25 |
26 | public class MainActivity extends BaseActivity implements AdapterView.OnItemSelectedListener {
27 |
28 | @BindView(R.id.spinner_devices)
29 | Spinner mSpinnerDevices;
30 | @BindView(R.id.spinner_baudrate)
31 | Spinner mSpinnerBaudrate;
32 | @BindView(R.id.btn_open_device)
33 | Button mBtnOpenDevice;
34 | @BindView(R.id.btn_send_data)
35 | Button mBtnSendData;
36 | @BindView(R.id.btn_load_list)
37 | Button mBtnLoadList;
38 | @BindView(R.id.et_data)
39 | EditText mEtData;
40 |
41 | private Device mDevice;
42 |
43 | private int mDeviceIndex;
44 | private int mBaudrateIndex;
45 |
46 | private String[] mDevices;
47 | private String[] mBaudrates;
48 |
49 | private boolean mOpened = false;
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 |
55 | mEtData.setTransformationMethod(new AllCapTransformationMethod(true));
56 |
57 | initDevice();
58 | initSpinners();
59 | updateViewState(mOpened);
60 | }
61 |
62 | @Override
63 | protected void onDestroy() {
64 | SerialPortManager.instance().close();
65 | super.onDestroy();
66 | }
67 |
68 | @Override
69 | protected boolean hasActionBar() {
70 | return false;
71 | }
72 |
73 | @Override
74 | protected int getLayoutId() {
75 | return R.layout.activity_main;
76 | }
77 |
78 | /**
79 | * 初始化设备列表
80 | */
81 | private void initDevice() {
82 |
83 | SerialPortFinder serialPortFinder = new SerialPortFinder();
84 |
85 | // 设备
86 | mDevices = serialPortFinder.getAllDevicesPath();
87 | if (mDevices.length == 0) {
88 | mDevices = new String[] {
89 | getString(R.string.no_serial_device)
90 | };
91 | }
92 | // 波特率
93 | mBaudrates = getResources().getStringArray(baudrates);
94 |
95 | mDeviceIndex = PrefHelper.getDefault().getInt(PreferenceKeys.SERIAL_PORT_DEVICES, 0);
96 | mDeviceIndex = mDeviceIndex >= mDevices.length ? mDevices.length - 1 : mDeviceIndex;
97 | mBaudrateIndex = PrefHelper.getDefault().getInt(PreferenceKeys.BAUD_RATE, 0);
98 |
99 | mDevice = new Device(mDevices[mDeviceIndex], mBaudrates[mBaudrateIndex]);
100 | }
101 |
102 | /**
103 | * 初始化下拉选项
104 | */
105 | private void initSpinners() {
106 |
107 | ArrayAdapter deviceAdapter =
108 | new ArrayAdapter(this, R.layout.spinner_default_item, mDevices);
109 | deviceAdapter.setDropDownViewResource(R.layout.spinner_item);
110 | mSpinnerDevices.setAdapter(deviceAdapter);
111 | mSpinnerDevices.setOnItemSelectedListener(this);
112 |
113 | ArrayAdapter baudrateAdapter =
114 | new ArrayAdapter(this, R.layout.spinner_default_item, mBaudrates);
115 | baudrateAdapter.setDropDownViewResource(R.layout.spinner_item);
116 | mSpinnerBaudrate.setAdapter(baudrateAdapter);
117 | mSpinnerBaudrate.setOnItemSelectedListener(this);
118 |
119 | mSpinnerDevices.setSelection(mDeviceIndex);
120 | mSpinnerBaudrate.setSelection(mBaudrateIndex);
121 | }
122 |
123 | @OnClick({ R.id.btn_open_device, R.id.btn_send_data, R.id.btn_load_list })
124 | public void onViewClicked(View view) {
125 | switch (view.getId()) {
126 | case R.id.btn_open_device:
127 | switchSerialPort();
128 | break;
129 | case R.id.btn_send_data:
130 | sendData();
131 | break;
132 | case R.id.btn_load_list:
133 | startActivity(new Intent(this, LoadCmdListActivity.class));
134 | break;
135 | }
136 | }
137 |
138 | private void sendData() {
139 |
140 | String text = mEtData.getText().toString().trim();
141 | if (TextUtils.isEmpty(text) || text.length() % 2 != 0) {
142 | ToastUtil.showOne(this, "无效数据");
143 | return;
144 | }
145 |
146 | SerialPortManager.instance().sendCommand(text);
147 | }
148 |
149 | /**
150 | * 打开或关闭串口
151 | */
152 | private void switchSerialPort() {
153 | if (mOpened) {
154 | SerialPortManager.instance().close();
155 | mOpened = false;
156 | } else {
157 |
158 | // 保存配置
159 | PrefHelper.getDefault().saveInt(PreferenceKeys.SERIAL_PORT_DEVICES, mDeviceIndex);
160 | PrefHelper.getDefault().saveInt(PreferenceKeys.BAUD_RATE, mBaudrateIndex);
161 |
162 | mOpened = SerialPortManager.instance().open(mDevice) != null;
163 | if (mOpened) {
164 | ToastUtil.showOne(this, "成功打开串口");
165 | } else {
166 | ToastUtil.showOne(this, "打开串口失败");
167 | }
168 | }
169 | updateViewState(mOpened);
170 | }
171 |
172 | /**
173 | * 更新视图状态
174 | *
175 | * @param isSerialPortOpened
176 | */
177 | private void updateViewState(boolean isSerialPortOpened) {
178 |
179 | int stringRes = isSerialPortOpened ? R.string.close_serial_port : R.string.open_serial_port;
180 |
181 | mBtnOpenDevice.setText(stringRes);
182 |
183 | mSpinnerDevices.setEnabled(!isSerialPortOpened);
184 | mSpinnerBaudrate.setEnabled(!isSerialPortOpened);
185 | mBtnSendData.setEnabled(isSerialPortOpened);
186 | mBtnLoadList.setEnabled(isSerialPortOpened);
187 | }
188 |
189 | @Override
190 | public void onItemSelected(AdapterView> parent, View view, int position, long id) {
191 |
192 | // Spinner 选择监听
193 | switch (parent.getId()) {
194 | case R.id.spinner_devices:
195 | mDeviceIndex = position;
196 | mDevice.setPath(mDevices[mDeviceIndex]);
197 | break;
198 | case R.id.spinner_baudrate:
199 | mBaudrateIndex = position;
200 | mDevice.setBaudrate(mBaudrates[mBaudrateIndex]);
201 | break;
202 | }
203 | }
204 |
205 | @Override
206 | public void onNothingSelected(AdapterView> parent) {
207 | // 空实现
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licheedev/serialtool/activity/base/BaseActivity.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool.activity.base;
2 |
3 | import android.os.Bundle;
4 | import androidx.fragment.app.FragmentManager;
5 | import androidx.appcompat.app.ActionBar;
6 | import androidx.appcompat.app.AppCompatActivity;
7 | import butterknife.ButterKnife;
8 | import com.licheedev.serialtool.R;
9 | import com.licheedev.serialtool.fragment.LogFragment;
10 | import com.licheedev.serialtool.comn.message.IMessage;
11 | import com.licheedev.serialtool.comn.message.LogManager;
12 | import org.greenrobot.eventbus.EventBus;
13 | import org.greenrobot.eventbus.Subscribe;
14 | import org.greenrobot.eventbus.ThreadMode;
15 |
16 | public abstract class BaseActivity extends AppCompatActivity {
17 |
18 | protected ActionBar mActionBar;
19 | private LogFragment mLogFragment;
20 |
21 | @Override
22 | protected void onCreate(Bundle savedInstanceState) {
23 | super.onCreate(savedInstanceState);
24 | setContentView(getLayoutId());
25 | ButterKnife.bind(this);
26 | if (hasActionBar()) {
27 | mActionBar = getSupportActionBar();
28 | }
29 | initFragment();
30 | }
31 |
32 | /**
33 | * 获取布局id
34 | *
35 | * @return
36 | */
37 | protected abstract int getLayoutId();
38 |
39 | @Override
40 | public void onStart() {
41 | super.onStart();
42 | EventBus.getDefault().register(this);
43 | }
44 |
45 | @Override
46 | public void onStop() {
47 | super.onStop();
48 | EventBus.getDefault().unregister(this);
49 | }
50 |
51 | @Override
52 | protected void onResume() {
53 | super.onResume();
54 | refreshLogList();
55 | }
56 |
57 | /**
58 | * 刷新日志列表
59 | */
60 | protected void refreshLogList() {
61 | mLogFragment.updateAutoEndButton();
62 | mLogFragment.updateList();
63 | }
64 |
65 | /**
66 | * 初始化日志Fragment
67 | */
68 | protected void initFragment() {
69 | FragmentManager fragmentManager = getSupportFragmentManager();
70 | mLogFragment = (LogFragment) fragmentManager.findFragmentById(R.id.log_fragment);
71 | }
72 |
73 | /**
74 | * 添加日志
75 | *
76 | * @param message
77 | */
78 | protected void addLog(IMessage message) {
79 | LogManager.instance().add(message);
80 | refreshLogList();
81 | }
82 |
83 | @Override
84 | public boolean onSupportNavigateUp() {
85 | finish();
86 | return super.onSupportNavigateUp();
87 | }
88 |
89 | protected boolean hasActionBar() {
90 | return true;
91 | }
92 |
93 | protected void setActionBar(boolean showUp, String title) {
94 | mActionBar.setHomeButtonEnabled(showUp);
95 | mActionBar.setDisplayHomeAsUpEnabled(showUp);
96 | mActionBar.setTitle(title);
97 | }
98 |
99 | protected void setActionBar(boolean showUp, int stringResId) {
100 | mActionBar.setHomeButtonEnabled(showUp);
101 | mActionBar.setDisplayHomeAsUpEnabled(showUp);
102 | mActionBar.setTitle(stringResId);
103 | }
104 |
105 | @Subscribe(threadMode = ThreadMode.MAIN)
106 | public void onMessageEvent(IMessage message) {
107 | // 收到时间,刷新界面
108 | mLogFragment.add(message);
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licheedev/serialtool/comn/Device.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool.comn;
2 |
3 | /**
4 | * 串口设备
5 | */
6 | public class Device {
7 |
8 | private String path;
9 | private String baudrate;
10 |
11 | public Device() {
12 | }
13 |
14 | public Device(String path, String baudrate) {
15 | this.path = path;
16 | this.baudrate = baudrate;
17 | }
18 |
19 | public String getPath() {
20 | return path;
21 | }
22 |
23 | public void setPath(String path) {
24 | this.path = path;
25 | }
26 |
27 | public String getBaudrate() {
28 | return baudrate;
29 | }
30 |
31 | public void setBaudrate(String baudrate) {
32 | this.baudrate = baudrate;
33 | }
34 |
35 | @Override
36 | public String toString() {
37 | return "Device{" + "path='" + path + '\'' + ", baudrate='" + baudrate + '\'' + '}';
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/licheedev/serialtool/comn/SerialPortManager.java:
--------------------------------------------------------------------------------
1 | package com.licheedev.serialtool.comn;
2 |
3 | import android.os.HandlerThread;
4 | import android.serialport.SerialPort;
5 | import com.licheedev.hwutils.ByteUtil;
6 | import com.licheedev.myutils.LogPlus;
7 | import com.licheedev.serialtool.comn.message.LogManager;
8 | import com.licheedev.serialtool.comn.message.SendMessage;
9 | import io.reactivex.Observable;
10 | import io.reactivex.ObservableEmitter;
11 | import io.reactivex.ObservableOnSubscribe;
12 | import io.reactivex.Observer;
13 | import io.reactivex.Scheduler;
14 | import io.reactivex.android.schedulers.AndroidSchedulers;
15 | import io.reactivex.disposables.Disposable;
16 | import java.io.File;
17 | import java.io.IOException;
18 | import java.io.OutputStream;
19 |
20 | /**
21 | * Created by Administrator on 2017/3/28 0028.
22 | */
23 | public class SerialPortManager {
24 |
25 | private static final String TAG = "SerialPortManager";
26 |
27 | private SerialReadThread mReadThread;
28 | private OutputStream mOutputStream;
29 | private HandlerThread mWriteThread;
30 | private Scheduler mSendScheduler;
31 |
32 | private static class InstanceHolder {
33 |
34 | public static SerialPortManager sManager = new SerialPortManager();
35 | }
36 |
37 | public static SerialPortManager instance() {
38 | return InstanceHolder.sManager;
39 | }
40 |
41 | private SerialPort mSerialPort;
42 |
43 | private SerialPortManager() {
44 | }
45 |
46 | /**
47 | * 打开串口
48 | *
49 | * @param device
50 | * @return
51 | */
52 | public SerialPort open(Device device) {
53 | return open(device.getPath(), device.getBaudrate());
54 | }
55 |
56 | /**
57 | * 打开串口
58 | *
59 | * @param devicePath
60 | * @param baudrateString
61 | * @return
62 | */
63 | public SerialPort open(String devicePath, String baudrateString) {
64 | if (mSerialPort != null) {
65 | close();
66 | }
67 |
68 | try {
69 | File device = new File(devicePath);
70 | int baurate = Integer.parseInt(baudrateString);
71 | mSerialPort = new SerialPort(device, baurate);
72 |
73 | mReadThread = new SerialReadThread(mSerialPort.getInputStream());
74 | mReadThread.start();
75 |
76 | mOutputStream = mSerialPort.getOutputStream();
77 |
78 | mWriteThread = new HandlerThread("write-thread");
79 | mWriteThread.start();
80 | mSendScheduler = AndroidSchedulers.from(mWriteThread.getLooper());
81 |
82 | return mSerialPort;
83 | } catch (Throwable tr) {
84 | LogPlus.e(TAG, "打开串口失败", tr);
85 | close();
86 | return null;
87 | }
88 | }
89 |
90 | /**
91 | * 关闭串口
92 | */
93 | public void close() {
94 | if (mReadThread != null) {
95 | mReadThread.close();
96 | }
97 | if (mOutputStream != null) {
98 | try {
99 | mOutputStream.close();
100 | } catch (IOException e) {
101 | e.printStackTrace();
102 | }
103 | }
104 |
105 | if (mWriteThread != null) {
106 | mWriteThread.quit();
107 | }
108 |
109 | if (mSerialPort != null) {
110 | mSerialPort.close();
111 | mSerialPort = null;
112 | }
113 | }
114 |
115 | /**
116 | * 发送数据
117 | *
118 | * @param datas
119 | * @return
120 | */
121 | private void sendData(byte[] datas) throws Exception {
122 | mOutputStream.write(datas);
123 | }
124 |
125 | /**
126 | * (rx包裹)发送数据
127 | *
128 | * @param datas
129 | * @return
130 | */
131 | private Observable