需求:
称重系统 实时获取称重数据并于平板显示屏上
功能描述:
使用android实现usb串口传输 并将数据传递于h5
实现:
1、使用android studio 创建安卓项目
a.选择项目名称、机构名称、项目存放路径、包名
b.选择项目的运行条件

c.选择入口Activity类型

d.配置入口类的信息,然后完成创建项目。到这一步一个安卓项目就创建成功了,运行一下感受一下

e.小调整,将项目切换成Project模式,使得项目结构变得直观

PS:如果这时候项目无法正常,可能的情况如下:
a.sdk路径没有配置或没有下载对应的sdk版本

b.因为我们是访问的国外服务器,如果项目初始化很慢(下面一直在转圈),说明是被墙了,可以使用国内镜像。到此我们就可以运行android项目了

上述截图的代码:(只修改我上面圈中的位置)
buildscript {
repositories {
maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'}
maven { url "https://jitpack.io" }
google()
// jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
maven{url 'http://maven.aliyun.com/nexus/content/groups/public/'}
google()
//maven { url 'https://maven.google.com' }
//jcenter()
maven { url 'https://jitpack.io' }
}
}
2、运行并修改android项目
a.先运行一下新项目

b.修改入口文件的布局样式,因为我们要使用H5的页面,所以需要搭建运行H5文件的布局(WebView相当于浏览器)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="zx.usb.com.MainActivity">
<WebView
android:id="@+id/webView_mainActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>c.修改MainActivity逻辑(缺少的类在下文会提到)
/**
* app的入口页面(首页)
* 文件路径:USBProject->app->src->main->assets->在这个里面就可以当自己的项目库了
*/
public class MainActivity extends AppCompatActivity {
//TODO 重要的位置 Begin 开始线-------------------------
private String mainUrl = "file:///android_asset/index.html";//TODO 入口页面路径
/**
* 1.H5调用android方法:只要有@JavascriptInterface这个注解的方法,H5都能通过(window.android.方法名) 调用
* 目前已公开的方法:1.closeApp(退出本页面)
* 2.toast(吐司文字)
* 3.openNewUrl(加载新的Url)
* 4.connectUSB(连接USB串口功能)
* 5.closeUSBConnect(关闭USB串口功能)
* <p>
* 2.android调用H5方法:webView.evaluateJavascript("javascript:H5的方法名",new ValueCallback<String>(){}),具体实现在本类的205行
* 目前在H5页面公开的方法:
* 1.usbNewData(当usb串口连接成功后,接收到新数据就会调用此方法)
*/
//TODO 重要的位置 End 结束线----------------------------
private final String TAG = "XuanJie";
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
webView = findViewById(R.id.webView_mainActivity);//初始化WebView控件
initWebView();//webview初始化方法
}
@SuppressLint("JavascriptInterface")
private void initWebView() {
WebSettings webSettings = webView.getSettings();
//@time 2018-01-04区分华为,华为不需要众筹支付
//设置自适应屏幕,两者合用
webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小
webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
webSettings.setJavaScriptEnabled(true);
//设置可以访问文件
webSettings.setAllowFileAccess(true);
webSettings.setAppCacheEnabled(true);
webSettings.setTextZoom(100);
//设置 缓存模式
webSettings.setCacheMode(WebSettings.LOAD_NO_CACHE);
// 设置可以使用localStorage
webSettings.setDomStorageEnabled(true);
webView.setWebViewClient(new WebViewClient());
webView.setWebChromeClient(new WebChromeClient());
webView.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
webView.goBack();
return true;
}
return false;
}
});
webView.addJavascriptInterface(new CourseNewJavaScriptInter(), "android");
webView.loadUrl(mainUrl);
}
class CourseNewJavaScriptInter {
/**
* 1.退出本页面
*/
@JavascriptInterface
public void closeApp() {
runOnUiThread(new Runnable() {
@Override
public void run() {
MainActivity.this.finish();
}
});
}
/**
* 2.吐司文字
*/
@JavascriptInterface
public void toast(final String toastStr) {
runOnUiThread(new Runnable() {
@Override
public void run() {
UIUtil.showToast(toastStr);
}
});
}
/**
* 3.加载新的Url
*
* @param url 新的Url路径
* @param isClearCache 是否清除缓存
*/
@JavascriptInterface
public void openNewUrl(final String url, final boolean isClearCache) {
runOnUiThread(new Runnable() {
@Override
public void run() {
if (isClearCache) {
webView.clearCache(true);
}
webView.loadUrl(url);
}
});
}
/**
* 4.连接USB串口功能
*/
@JavascriptInterface
public void connectUSB() {
runOnUiThread(new Runnable() {
@Override
public void run() {
USBUtil.connectUSB(mListener);
}
});
}
/**
* 5.关闭USB串口功能
*/
@JavascriptInterface
public void closeUSBConnect() {
runOnUiThread(new Runnable() {
@Override
public void run() {
USBUtil.closeUSBConnect();
UIUtil.showToast("USB连接已关闭");
}
});
}
}
class CourseWebViewClient extends WebViewClient {
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
UIUtil.showToast("加载出错onReceivedError");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return;
}
webView.setVisibility(View.GONE);
}
@TargetApi(Build.VERSION_CODES.M)
@Override
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
super.onReceivedError(view, request, error);
UIUtil.showToast("加载出错onReceivedError");
if (request.isForMainFrame()) {
// 在这里显示自定义错误页
webView.setVisibility(View.GONE);
}
}
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.startsWith("tel:")) {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
startActivity(intent);
} else {
view.loadUrl(url);
}
return true;
}
@Override
public void onPageFinished(WebView view, String url) {
CookieManager cookieManager = CookieManager.getInstance();
String CookieStr = cookieManager.getCookie(url);
super.onPageFinished(view, url);
// LogUtils.i("cookie内容:" + CookieManager.getInstance().getCookie(url));
}
}
//连接串口的监听,当收到新消息就会调用onNewData
private SerialInputOutputManager.Listener mListener = new SerialInputOutputManager.Listener() {
@Override
public void onRunError(Exception e) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
UIUtil.showToast("onRunError");
}
});
}
@Override
public void onNewData(final byte[] data) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
//TODO 收到新的数据--------操作区 Begin
webView.loadUrl("javascript:usbNewData('" + new String(data) + "')"); //TODO 收到新的数据--------操作区 End
}
});
}
};
@Override
protected void onDestroy() {
super.onDestroy();
USBUtil.closeUSBConnect();
}
}d.UIUtil类
public class UIUtil {
private static Toast toast;
/**
* 吐司消息
*/
public static void showToast(String content) {
if (toast == null) {
toast = Toast.makeText(MyApplication.getContext(), content, Toast.LENGTH_SHORT);
} else {
toast.setText(content);
toast.setDuration(Toast.LENGTH_SHORT);
}
toast.show();
}
}3、usb串口传输核心代码编写
a.首先我们需要引入usb串口的module包,可以在我的百度云盘下载:
提取码:2bz9
用法:


最后需要配置一下,代码:implementation project(':usbSerialForAndroid')

b.USB串口操作类:USBUtil
public class USBUtil {
private static final String TAG = "CYX";
public static final String INTENT_ACTION_GRANT_USB = BuildConfig.APPLICATION_ID + ".GRANT_USB";
private static UsbSerialPort port; /** * 连接USB */
public static void connectUSB(SerialInputOutputManager.Listener mListener) {
try {
if (port != null) {
UIUtil.showToast("设备处于已连接状态");
return;
}
UsbManager manager = (UsbManager) MyApplication.getContext().getSystemService(Context.USB_SERVICE);
List<UsbSerialDriver> availableDrivers = UsbSerialProber.getDefaultProber().findAllDrivers(manager);
if (availableDrivers.isEmpty()) {
UIUtil.showToast("当前连接设备为空");
return;
}
UsbSerialDriver driver = availableDrivers.get(0);
UsbDeviceConnection connection = manager.openDevice(driver.getDevice());
if (connection == null) {
// You probably need to call UsbManager.requestPermission(driver.getDevice(), ..)
if (!manager.hasPermission(driver.getDevice())) {
PendingIntent usbPermissionIntent = PendingIntent.getBroadcast(MyApplication.getContext(), 0, new Intent(INTENT_ACTION_GRANT_USB), 0);
manager.requestPermission(driver.getDevice(), usbPermissionIntent);
}
return;
}
port = driver.getPorts().get(0);
port.open(connection);
port.setParameters(9600, 8, UsbSerialPort.STOPBITS_1, UsbSerialPort.PARITY_NONE);
mSerialIoManager = new SerialInputOutputManager(port, mListener);//添加监听
//在新的线程中监听串口的数据变化
mExecutor.submit(mSerialIoManager);
UIUtil.showToast("连接成功");
} catch (Exception e) {
UIUtil.showToast(e.getMessage());
}
}
private static final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private static SerialInputOutputManager mSerialIoManager;
public static void closeUSBConnect() {
if (mSerialIoManager != null) {
mSerialIoManager.stop();
mSerialIoManager = null;
}
try {
if (port != null) {
port.close();
port = null;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}4、android与h5通信
1.android -> h5
在连接串口的监听方法中,调用h5方法
public void onNewData(final byte[] data) {
MainActivity.this.runOnUiThread(new Runnable() {
@Override
public void run() {
//TODO 收到新的数据--------操作区 Begin
webView.loadUrl("javascript:usbNewData('" + new String(data) + "')");
//TODO 收到新的数据--------操作区 End
}
});
}2.h5 -> android
在main -> assets(需要创建此目录) -> 创建html文件(index.html此处名字可以自己定义)

核心代码如下
<div class="btn" id="btnExit">退出</div>
<div class="btn" id="toast">吐司文字</div>
<div class="btn" id="connect">连接USB</div>
<script>
window.onload=function(){
//PS:下面这种就是调用安卓原生的方法:window.android.原生声明的方法
var btnExit=document.getElementById("btnExit");//退出按钮
btnExit.onclick=function(){
window.android.closeApp();//调用android声明的closeApp方法
}
var btnToast=document.getElementById("toast");
btnToast.onclick=function(){
window.android.toast("啦啦啦啦啦!!!"); //吐司文字
}
var connect=document.getElementById("connect");//连接USB
connect.onclick=function(){
window.android.connectUSB(); //调用android生命的connectUSB方法
}
}
//PS:下面这种就是原生android调用H5的方法,
//此方法当usb串口有返回数据的时候就会调用这个方法
function usbNewData(str){
//str是移动端传递过来的
window.android.toast("收到新数据:" + new String(data));
}
</script>tips:
- 调用安卓原生的方法:
window.android.原生声明的方法 - window下的方法,安卓可调用到,但如果是vue项目,需在create生命周期中将方法全局定义下
created() {
window.usbNewData = this.usbNewData
}