博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
10055自动进刀水钻机android蓝牙2.0SSP项目源码结构使用说明【版本更新、自动连接、控件批量处理、接收解析】
阅读量:2389 次
发布时间:2019-05-10

本文共 12545 字,大约阅读时间需要 41 分钟。

一、简介

截图(蓝牙查找界面,帮助界面,主控制界面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() {	            @Override	            public void run() {	               	//线程	                runOnUiThread(new Runnable() {	                    @Override	                    public 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方面需要指定GB2312				os.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-8											num=0;										}									}								}								else									smsg+=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

定时1秒发送

private void setElse() //设置其它控件		    {				toggleButton_switch3 = (ToggleButton) findViewById(R.id.ToggleButton03);				toggleButton_switch3.setChecked(false); // 显示原状态 				toggleButton_switch3.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {							// @Override							public 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); // 显示原状态 					            	}								}								else									Toast.makeText(MainActivity.this, "关闭定时发送", Toast.LENGTH_SHORT).show();															}						});		    }		  Handler handler2 = new Handler();			Runnable runnable2 = new Runnable() {				@Override				public 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.3tf:00.2rs:023zf:02.2Aqs:024Stztkmtyx

接收解析源码 

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
15 && (smsg.endsWith("yx\r\n") || smsg.endsWith("wx\r\n"))) { for(int i=0;i

 

五、详解——版本更新

版本更新需要的权限

    <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			 * 			 */			@Override			public 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的该包名的Intent			Intent resolveIntent = new Intent(Intent.ACTION_MAIN, null);			resolveIntent.addCategory(Intent.CATEGORY_LAUNCHER);			resolveIntent.setPackage(packageinfo.packageName);			// 通过getPackageManager()的queryIntentActivities方法遍历			List
resolveinfoList = getPackageManager() .queryIntentActivities(resolveIntent, 0); ResolveInfo resolveinfo = resolveinfoList.iterator().next(); if (resolveinfo != null) { // packagename = 参数packname String packageName = resolveinfo.activityInfo.packageName; // 这个就是我们要找的该APP的LAUNCHER的Activity[组织形式:packagename.mainActivityname] String className = resolveinfo.activityInfo.name; // LAUNCHER Intent Intent 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); } }

 

转载地址:http://scaab.baihongyu.com/

你可能感兴趣的文章
写一个块设备驱动-第9章
查看>>
JDBC数据库开发技术
查看>>
oracle表分区详解
查看>>
从头做leetcode之leetcode 5 最长回文子串
查看>>
从头做leetcode之leetcode 6 Z字形变换
查看>>
将无符号偏移量添加到...溢出到...
查看>>
从头做leetcode之leetcode 11 盛最多水的容器
查看>>
设计模式、框架和架构的联系
查看>>
安装VMware虚拟机
查看>>
常用的设计模式和代码
查看>>
桥接模式-通俗的理解(转)
查看>>
MXML 文件中的xmlns是什么意思?
查看>>
Flex Builder 中的工作空间、项目
查看>>
Flex 获得远程数据
查看>>
Flex 添加效果的两种方法
查看>>
Flash Builder 4字体设置
查看>>
Actionscript 3.0 笔记一
查看>>
图像处理库OpenCV参考网址
查看>>
dllimport与dllexport作用与区别
查看>>
OpenGL坐标系
查看>>