本文共 12545 字,大约阅读时间需要 41 分钟。
1.蓝牙连接、断开、接收数据、发送数据、自动连接。
2.指令发送按钮、拖动条、加减微调,定时发送。接收解析并设定参数到界面。
3.版本更新。含检查新版本,下载,安装。
4.帮助。使用说明、购买链接、版本等。
─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.版本更新等其它功能。
<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
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;i15 && (smsg.endsWith("yx\r\n") || smsg.endsWith("wx\r\n"))) { for(int i=0;i
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(); }
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方法遍历 ListresolveinfoList = 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/