10055自动进刀水钻机android蓝牙2.0SSP项目源码结构使用说明【版本更新、自动连接、控件批量处理、接收解析】

本文主要是介绍10055自动进刀水钻机android蓝牙2.0SSP项目源码结构使用说明【版本更新、自动连接、控件批量处理、接收解析】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、简介

截图(蓝牙查找界面,帮助界面,主控制界面1,主控制界面2,主控制界面3)

        

开发环境:Eclipse adt。

主要功能:

1.蓝牙连接、断开、接收数据、发送数据、自动连接。

2.指令发送按钮、拖动条、加减微调,定时发送。接收解析并设定参数到界面。

3.版本更新。含检查新版本,下载,安装。

4.帮助。使用说明、购买链接、版本等。

(一定要允许打开蓝牙、定位、读写存储、网络权限,APP提示时不要拒绝)

对应源码: 

─src 
    ├─com
    │  ├─iswitch
    │  │  └─iswitch
    │  │          AboutActivity.java//帮助界面
    │  │          DeviceListActivity.java//蓝牙查找界面
    │  │          MainActivity.java //主控制界面(含蓝牙连接、断开、收发数据)
    │  │          
    │  └─trinet
    │      └─util
    │              AppUtils.java //获得app通用信息,包名、版本等
    │              DialogHelper.java //对话框通用范例,下载对话框等
    │              NetHelper.java //网络通用处理 
    │              
    ├─SilentInstall
    │      SilentInstall.java //安装处理
    │      
    └─update
        └─test
                UpdateManager.java //更新管理处理
                Update_TestActivity.java //检查更新界面(启动界面)

二、开发的顺序

1.先通用蓝牙连接、断开、接收数据、发送数据稳定。基础没弄好就不能做后面的。

      有些蓝牙模块不一样,2.0与4.0不是一个结构,不是改改就行。

2.界面确定。xml文件确定好。

3.修改源码。源码是根据界面来写,所以先要确定界面。指令发送按钮、拖动条、加减微调,定时发送。接收解析设定参数。

4.版本更新等其它功能。

阅读源码是根据界面xml来找源码。源码都有注释。

 

三、详解——蓝牙连接、断开、接收数据、发送数据

蓝牙需要的权限

  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission>

ACCESS_COARSE_LOCATION没有,6.0及以上的手机会搜不到蓝牙模块。

自动连接逻辑:

连接成功后保存连接地址,再次打开APP时直接从已经配对的设备中找保存的连接地址,找到就直接返回地址去连接。没有找到就扫描。设备不在就删除保存的地址再扫描。扫描时能搜到的设备才显示出来(配对了不在线也不显示)。第一次需手工配对连接。自动连接连接的是上一次连接成功的设备。

连接逻辑:

放到线程中,UI更新放到UI线程。所以不死机。连接中再点连接会显示“请等待”。

	        new Thread(new Runnable() {@Overridepublic void run() {//线程runOnUiThread(new Runnable() {@Overridepublic void run() {//线程结束后要更新的UI的线程}});}}).start();

连接成功后,注册异常断开接收器,启动接收。

发送处理

	// 发送响应public boolean sendString(String str) {if (_socket == null) {Toast.makeText(this, "未连接蓝牙", Toast.LENGTH_SHORT).show();return false;}if (str == null) {Toast.makeText(this, "发送内容为空", Toast.LENGTH_SHORT).show();return false;}try {OutputStream os = _socket.getOutputStream(); // 蓝牙连接输出流if(hex){byte[] bos_hex = hexStringToBytes(str); // 十六进制os.write(bos_hex);}else{	byte[] bos = str.getBytes("GBK");	//native的Socket发送字节流默认是GB2312的,所以在Java方面需要指定GB2312os.write(bos);Toast.makeText(this, "发送成功"+str, Toast.LENGTH_SHORT).show();return true;}} catch (IOException e) {return false;}return true;}

接收处理

	// 接收数据线程Thread ReadThread = new Thread() {public void run() {int num = 0;byte[] buffer = new byte[1024];byte[] buffer_new = new byte[1024];bRun = true;// 接收线程while (true) {try {while (is.available() == 0) {        //无接收数据while (bRun == false) {   //线程阻塞}}while (true) {if (1 == is.read(buffer, 0, 1)) // 一个一个地接收,把需要的数据放在buffer_new中    {if(hex){smsg+=String.format("%02X ",buffer[0]);//转为十六进制格式 所有接收}else{								if(127<(buffer[0]&0xff)) //解决汉字被截断{if (1 == is.read(buffer, 1, 1))   //昇954E 4E =78 少于128  所以只能判断第一个字节{smsg+=new String(buffer, 0, 2, "GBK");  //+String.format("(%02X %02X )",buffer_new[0],buffer_new[1]);  //GBK   GBK  UTF-8}else{buffer_new[num++]=buffer[0];if(num==2){smsg+=new String(buffer_new, 0, 2, "GBK");  //+String.format("(%02X %02X )",buffer_new[0],buffer_new[1]);  //GBK   GBK  UTF-8num=0;}}}elsesmsg+=new String(buffer, 0, 1, "GBK");  //GBK   GBK  UTF-8}}/*num = is.read(buffer); // 读入数据smsg += new String(buffer, 0, num, "GBK");  //GBK   GBK  UTF-8*/if (is.available() == 0)break; // 短时间没有数据才跳出进行显示}// 发送显示消息,进行显示刷新handler.sendMessage(handler.obtainMessage());} catch (IOException e) {}}}};

四、详解——指令发送按钮、拖动条、加减微调,定时发送。接收解析设定参数。

主要是批量处理(控件绑定,控件响应,控件指令,接收解析设定参数到界面)

控件批量处理范例

	private String[][] toggleCommandArray={{"ty","tz"},                        			                        			                     {"tg","tk"},      //关闭,打开                			                   {"at","mt"},                       {"wx","yx"},}; private int[] toggleIdArray={R.id.ToggleButton11,R.id.ToggleButton12,R.id.ToggleButton01,R.id.ToggleButton02,};private ToggleButton[] toggleArray=new ToggleButton[toggleIdArray.length];private void setElse() //设置其它控件{				for(int i=0;i<toggleIdArray.length;i++){toggleArray[i] = (ToggleButton) findViewById(toggleIdArray[i]);toggleArray[i].setChecked(false); // 显示原状态 退刀关闭水钻机toggleArray[i].setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {// @Overridepublic void onCheckedChanged(CompoundButton v,boolean isChecked) {//判断是不是点击触发的,否则当我setChecked()时会触发此listenerif(isToggle)return; for(int i=0;i<toggleIdArray.length;i++){if(v.getId()==toggleIdArray[i]){if (isChecked) {sendString(toggleCommandArray[i][1]);}		else{sendString(toggleCommandArray[i][0]);}}}}});}}

定时1秒发送

		 private void setElse() //设置其它控件{toggleButton_switch3 = (ToggleButton) findViewById(R.id.ToggleButton03);toggleButton_switch3.setChecked(false); // 显示原状态 toggleButton_switch3.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {// @Overridepublic void onCheckedChanged(CompoundButton buttonView,boolean isChecked) {if (isChecked) {if (_socket != null) {Toast.makeText(MainActivity.this, "启动定时发送", Toast.LENGTH_SHORT).show();handler2.postDelayed(runnable2,TIME);}else{Toast.makeText(MainActivity.this, "请先连接设备", Toast.LENGTH_SHORT).show();toggleButton_switch3.setChecked(false); // 显示原状态 }}elseToast.makeText(MainActivity.this, "关闭定时发送", Toast.LENGTH_SHORT).show();}});}Handler handler2 = new Handler();Runnable runnable2 = new Runnable() {@Overridepublic void run() {// handler自带方法实现定时器try {if(toggleButton_switch3.isChecked()){		smsg="";dis.setText(smsg);//先清空接收区sendString(btCommandArray[0]);handler2.postDelayed(runnable2,TIME);	   //定时到后,重新定时         		}else{}} catch (Exception e) {// e.printStackTrace();// System.out.println("exception...");}}};

接收解析

接收测试数据示例:

sz:00.3
tf:00.2
rs:023
zf:02.2A
qs:024S
tz
tk
mt
yx

接收解析源码 

		    private void receive(String smsg){try {//Toast.makeText(this, "接收:"+smsg, Toast.LENGTH_SHORT).show();if (smsg.contains(":") && smsg.endsWith("\r\n") &&smsg.length()>5) {						for(int i=0;i<seekBarIdArray.length;i++){String s = valueCommandPreArray[i]+":"; //前缀,格式sz:00.0\r\nif (smsg.contains(s)) {int beginIndex = smsg.lastIndexOf(s);int endIndex = smsg.indexOf("\r\n",beginIndex + s.length()); //后缀String strData=smsg.substring(beginIndex + s.length(), endIndex).replace("A", "").replace("S", "");//取得的数据if(i<4 || i==6 || i==9)//这些有一位小数{valueArray[i]=(int)(10*Float.parseFloat(strData));}else{valueArray[i]=Integer.parseInt(strData);}tvsbSetText(i);	sbArray[i].setProgress(valueArray[i] );}}						}} catch (Exception e) {}try {//toggle开关if (smsg.length()>15 && (smsg.endsWith("yx\r\n") || smsg.endsWith("wx\r\n"))) {	for(int i=0;i<toggleIdArray.length;i++){isToggle=true;if (smsg.contains(toggleCommandArray[i][0]+"\r\n"))//ty off{toggleArray[i].setChecked(false);// toggleArray[i].invalidate();}else  if(smsg.contains(toggleCommandArray[i][1]+"\r\n"))//tz on{toggleArray[i].setChecked(true);//toggleArray[i].validate();}isToggle=false;}}} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}}

 

五、详解——版本更新

版本更新需要的权限


    <uses-permission android:name="android.permission.INTERNET"></uses-permission>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
  <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

逻辑顺序:

1.先检查更新。读这个版本说明文件Update_Test_version.txt内容 ,里面的版本与APP版本不一致就提示更新。所以版本说明文件Update_Test_version.txt内容 版本号要与更新的MainActivitySZJ.apk版本一致。verCode一致就行。android手机不允许更新的版本小于当前安装版本。无网络、网络处理失败和用户取消更新,就跳过。

[{"verCode":"4","verName":"1.4"}]

2. 下载apk文件。下载完后当前是放在手机根目录下。

3.安装。先静默安装(全自动,需root权限) ,不具备或失败就使用提示安装。

4.启动 新安装的apk(静默安装才有效)。

版本更新配置修改

检查更新

	private void getCurVersion() { //得到当前版本号try {PackageInfo pInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);curVersion = pInfo.versionName;curVersionCode = pInfo.versionCode;} catch (NameNotFoundException e) {Log.e("update", e.getMessage());curVersion = "1.1.1000";curVersionCode = 111000;}}public void checkUpdate() {		//检查是否有更新hasNewVersion = false;new Thread(){// ***************************************************************/*** @by wainiwann add* */@Overridepublic void run() {Log.i("@@@@@", ">>>>>>>>>>>>>>>>>>>>>>>>>>>getServerVerCode() ");try {String verjson = NetHelper.httpStringGet(UPDATE_CHECKURL);Log.i("@@@@", verjson+ "**************************************************");JSONArray array = new JSONArray(verjson);if (array.length() > 0) {JSONObject obj = array.getJSONObject(0);try {newVersionCode = Integer.parseInt(obj.getString("verCode"));newVersion = obj.getString("verName");updateInfo = "";Log.i("newVerCode", newVersionCode+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");Log.i("newVerName", newVersion+ "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");if (newVersionCode > curVersionCode) {hasNewVersion = true; //有新版本,需更新}} catch (Exception e) {newVersionCode = -1;newVersion = "";updateInfo = "";}}} catch (Exception e) {Log.e("update", e.getMessage());}updateHandler.sendEmptyMessage(UPDATE_CHECKCOMPLETED);};// ***************************************************************}.start();}

下载apk

	public void downloadPackage() {new Thread() {			@Override  public void run() {  try {  URL url = new URL(UPDATE_DOWNURL);  HttpURLConnection conn = (HttpURLConnection)url.openConnection();  conn.connect();  int length = conn.getContentLength();  InputStream is = conn.getInputStream();  File ApkFile = new File(savefolder,UPDATE_SAVENAME);if(ApkFile.exists()){ApkFile.delete();}FileOutputStream fos = new FileOutputStream(ApkFile);  int count = 0;  byte buf[] = new byte[512];  do{  int numread = is.read(buf);  count += numread;  progress =(int)(((float)count / length) * 100);  updateHandler.sendMessage(updateHandler.obtainMessage(UPDATE_DOWNLOADING)); if(numread <= 0){      updateHandler.sendEmptyMessage(UPDATE_DOWNLOAD_COMPLETED);break;  }  fos.write(buf,0,numread);  }while(!canceled);  if(canceled){updateHandler.sendEmptyMessage(UPDATE_DOWNLOAD_CANCELED);}fos.close();  is.close();  } catch (MalformedURLException e) {  e.printStackTrace(); updateHandler.sendMessage(updateHandler.obtainMessage(UPDATE_DOWNLOAD_ERROR,e.getMessage()));} catch(IOException e){  e.printStackTrace();  updateHandler.sendMessage(updateHandler.obtainMessage(UPDATE_DOWNLOAD_ERROR,e.getMessage()));}  } }.start();}

提示安装

		private void installSelf(String fileName ) {//提示安装try{Intent intent = new Intent(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(new File(fileName)), "application/vnd.android.package-archive"); startActivity(intent);}catch(Exception e){				}			}

启动

	private void doStartApplicationWithPackageName(String packagename) {// 通过包名获取此APP详细信息,包括Activities、services、versioncode、name等等PackageInfo packageinfo = null;try {packageinfo = getPackageManager().getPackageInfo(packagename, 0);} catch (NameNotFoundException e) {e.printStackTrace();}if (packageinfo == null) {return;}// 创建一个类别为CATEGORY_LAUNCHER的该包名的IntentIntent resolveIntent = new Intent(Intent.ACTION_MAIN, null);resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);resolveIntent.setPackage(packageinfo.packageName);// 通过getPackageManager()的queryIntentActivities方法遍历List<ResolveInfo> resolveinfoList = getPackageManager().queryIntentActivities(resolveIntent, 0);ResolveInfo resolveinfo = resolveinfoList.iterator().next();if (resolveinfo != null) {// packagename = 参数packnameString packageName = resolveinfo.activityInfo.packageName;// 这个就是我们要找的该APP的LAUNCHER的Activity[组织形式:packagename.mainActivityname]String className = resolveinfo.activityInfo.name;// LAUNCHER IntentIntent intent = new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_LAUNCHER);// 设置ComponentName参数1:packagename参数2:MainActivity路径ComponentName cn = new ComponentName(packageName, className);intent.setComponent(cn);startActivity(intent);}}

 

这篇关于10055自动进刀水钻机android蓝牙2.0SSP项目源码结构使用说明【版本更新、自动连接、控件批量处理、接收解析】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/157087

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

Zookeeper安装和配置说明

一、Zookeeper的搭建方式 Zookeeper安装方式有三种,单机模式和集群模式以及伪集群模式。 ■ 单机模式:Zookeeper只运行在一台服务器上,适合测试环境; ■ 伪集群模式:就是在一台物理机上运行多个Zookeeper 实例; ■ 集群模式:Zookeeper运行于一个集群上,适合生产环境,这个计算机集群被称为一个“集合体”(ensemble) Zookeeper通过复制来实现

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

如何用Docker运行Django项目

本章教程,介绍如何用Docker创建一个Django,并运行能够访问。 一、拉取镜像 这里我们使用python3.11版本的docker镜像 docker pull python:3.11 二、运行容器 这里我们将容器内部的8080端口,映射到宿主机的80端口上。 docker run -itd --name python311 -p

poj3468(线段树成段更新模板题)

题意:包括两个操作:1、将[a.b]上的数字加上v;2、查询区间[a,b]上的和 下面的介绍是下解题思路: 首先介绍  lazy-tag思想:用一个变量记录每一个线段树节点的变化值,当这部分线段的一致性被破坏我们就将这个变化值传递给子区间,大大增加了线段树的效率。 比如现在需要对[a,b]区间值进行加c操作,那么就从根节点[1,n]开始调用update函数进行操作,如果刚好执行到一个子节点,

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in

hdu1689(线段树成段更新)

两种操作:1、set区间[a,b]上数字为v;2、查询[ 1 , n ]上的sum 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<queue>#include<set>#include<map>#include<stdio.h>#include<stdl

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听