前言


上篇文章我们介绍到了开发经典蓝牙和单片机通讯的过程,安卓通讯之《蓝牙单片机通讯助手》①集成工作 ,我们这里还要兼容最新的安卓6.0及以上的系统,因为从6.0以后的权限机制和以往的不一样,我们需要在代码中向用户询问权限。而且在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限,这是谷歌加的~~,所以我们先把运行的权限弄好先,再扫描设备、连接设备和双向通讯。


权限问题(兼容安卓6.0及以上)


很多小伙伴问我,为什么你以前写的安卓高版本的蓝牙App现在在高版本的安卓机就不可以获取到附近的蓝牙设备啦?这也是我上面提到的:在6.0运行蓝牙,还需要加上获取到此刻的地理位置的权限。所以我们先把权限弄好。我下面是用郭神封装好的权限代码。你们如有不懂,去CSDN搜索郭霖6.0就有当天的直播权限封装视频解说。


private void checkpermission() {//判断是否APi等于或大于23,即6.0if(Build.VERSION.SDK_INT >= 23){//所需要的权限加进去requestRuntimePermission(new String[]{Manifest.permission.ACCESS_COARSE_LOCATION,Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.BLUETOOTH,Manifest.permission.BLUETOOTH_ADMIN}, new PermissionListener() {//权限回调@Overridepublic void onGranted() {//全部都授权就跳转下个ActivityLog.i("权限全部都同意了","-----=="); }//某个没有被授权则强制退出程序@Overridepublic void onDenied(List<String> deniedPermission) {for(String persmission:deniedPermission){Toast.makeText(SplashActivity.this,"基于你的安卓版本大于6.0,未授予该权限导致不能运行,强制退出:"+persmission,Toast.LENGTH_LONG).show();} }});}else {//手机在6.0以下,则不需要获取到地理位置权限,直接跳过}}

打开蓝牙扫描工作


打开蓝牙工作,相信大家都会,用户在此打开蓝牙的是否,要用 intent回调。在这里,我们还需要写进一个蓝牙广播接收器,来监听系统发出来的已经搜索到的附近蓝牙设备,并且获取该数据的名字和蓝牙地址。


代码部分说明:

我这里用的是一个listVIew控件来展示当前可以连接的蓝牙设备。点击某一项,则把该项的蓝牙名字和id传下去到一个Activity的通讯工作。中间的是一个进度条之类的,自定义控件。


package com.example.xuhong.bluetoothassistant_master;import android.app.Activity;
import android.app.ListActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.widget.ListView;
import android.widget.TextView;import com.example.xuhong.bluetoothassistant_master.adapter.DeviceListAdapter;
import com.example.xuhong.bluetoothassistant_master.ui.WhorlView;public class DeviceScanActivity extends ListActivity {private TextView mtv_show;// 调试用private static final String TAG = "DeviceScanActivity";// 开启蓝牙请求码private static final int REQUEST_ENABLE = 0;// 停止扫描蓝牙消息头private static final int WHAT_CANCEL_DISCOVERY = 1;// 判断蓝牙列表private static final int WHAT_DEVICE_UPDATE = 2;// 扫描间隔时间private static final int SCAN_PERIOD = 30 * 1000;//实例化Adapterprivate DeviceListAdapter mLeDeviceListAdapter = null;// 蓝牙适配器private BluetoothAdapter mBluetoothAdapter = null;// 进度条private WhorlView mWhorlView = null;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_device_scan);init();}@Overrideprotected void onStart() {super.onStart();mLeDeviceListAdapter = new DeviceListAdapter(this);// 设置列表适配器,注:调用此方法必须继承ListActivitysetListAdapter(mLeDeviceListAdapter);scanDevice(true);mWhorlView.setVisibility(View.VISIBLE);}@Overrideprotected void onPause() {scanDevice(false);super.onPause();}@Overrideprotected void onDestroy() {unregReceiver();super.onDestroy();}//回调函数@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {if ( resultCode == Activity.RESULT_CANCELED) {finish();Process.killProcess(Process.myPid());}switch (requestCode){case REQUEST_ENABLE:if ( mBluetoothAdapter.isEnabled()) {registerReceiver();scanDevice(true);}break;}}@Overrideprotected void onListItemClick(ListView l, View v, int position, long id) {super.onListItemClick(l, v, position, id);BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);if (device == null) {return;}Intent intent = new Intent(this, MainActivity.class);Bundle bundle = new Bundle();bundle.putParcelable("device", device);intent.putExtras(bundle);scanDevice(false);startActivity(intent);finish();}/*** 消息处理者*/private Handler mHandler = new Handler(new Callback() {@Overridepublic boolean handleMessage(Message msg) {switch (msg.what) {case WHAT_DEVICE_UPDATE:mLeDeviceListAdapter.addDevice((BluetoothDevice) msg.obj);// 刷新列表mLeDeviceListAdapter.notifyDataSetChanged();break;case WHAT_CANCEL_DISCOVERY:mWhorlView.setVisibility(View.GONE);mtv_show.setText("搜索完毕!");break;default:break;}return false;}});/*** 初始化*/private void init() {mtv_show= (TextView) findViewById(R.id.tv_show);mWhorlView = (WhorlView) findViewById(R.id.whorl_view);// 开启动画mWhorlView.start();// 初始化本地蓝牙设备mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();// 检测蓝牙设备是否开启,如果未开启,发起Intent并回调if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);startActivityForResult(enableIntent, REQUEST_ENABLE);}registerReceiver();}/*** 是否扫描蓝牙设备*/private void scanDevice(boolean enable) {if (enable) {Log.d(TAG, "[1]--> startDiscovery()");// 开启扫描mBluetoothAdapter.startDiscovery();// 延时30s后取消扫描动作mHandler.postDelayed(new Runnable() {@Overridepublic void run() {mBluetoothAdapter.cancelDiscovery();Log.d(TAG, "[2]--> cancelDiscovery()");// 发送消息mHandler.sendEmptyMessage(WHAT_CANCEL_DISCOVERY);}}, SCAN_PERIOD);} else {Log.d(TAG, "[3]--> cancelDiscovery()");// 停止扫描mBluetoothAdapter.cancelDiscovery();}}/*** 注册广播接收器*/private void registerReceiver() {registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_FOUND));}/*** 注销广播接收器*/private void unregReceiver() {if (mReceiver != null) {unregisterReceiver(mReceiver);}}/*** 广播接收器接收返回的蓝牙信息*/private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();//未配对的设备if (BluetoothDevice.ACTION_FOUND == action) {BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);Log.d(TAG, "[4] --> " + device.getName() + "------" + device.getAddress());if (device != null) {//发送消息mHandler.sendMessage(mHandler.obtainMessage(WHAT_DEVICE_UPDATE, device));}}}};
}


通讯工作


我这里用的是一个spinner控件和一个button,怎么使用去看看相关博文,我这里只是供用户选择发送到哪一个数据。因为单片机通讯都是基本的 十六进制 0x00格式,所以我这里只是规范下发送的格式,你们也可以完全按照自己的想法设计UI。



import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.app.Activity;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;import android.os.Handler;
import android.os.Message;
import android.util.Log;import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.ToggleButton;import com.example.xuhong.bluetoothassistant_master.adapter.ChatListAdapter;
import com.example.xuhong.bluetoothassistant_master.data.ChatListData;
import com.example.xuhong.bluetoothassistant_master.util.Toaster;// 2016.09.15.   徐宏 编写
public class MainActivity extends Activity  {private ListView mChatListView;//列表private List<ChatListData> mList = new ArrayList<>();private ChatListAdapter chatListAdapter;private static final String TAG = "MainActivity";// uuidprivate static final String SPP_UUID = "00001101-0000-1000-8000-00805F9B34FB";private Spinner mSpinner;private String[] data = new String[]{"选择 0x01", "选择 0x02", "选择 0x03", "选择       0x04", "选择 0x05", "选择 0x06", "选择 0x07" , "选择 0x08", "选择 0x09"};private Byte sendData = null;private String mRecieve =null;private ArrayAdapter<String>  adapter;//获得系统的适配器private BluetoothDevice mBluetoothDevice = null;//创建socketprivate BluetoothSocket mSocket = null;//io流private OutputStream mOutS = null;private static  final  int CONNECT_SUCCED =10;private static final  int  mRecieve_SUCCED = 20;private InputStream input  =null;protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//全屏显示requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);setContentView(R.layout.activity_main);initview();}private void initview() {mSpinner = (Spinner) findViewById(R.id.mSpinner);mChatListView = (ListView)findViewById(R.id.mChatListView);chatListAdapter=new ChatListAdapter(MainActivity.this,mList);mChatListView.setAdapter(chatListAdapter);adapter= new ArrayAdapter<String>(this,android.R.layout.simple_list_item_1,data);mSpinner.setAdapter(adapter);mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> adapterView, View view, int id, long l) {switch (id){case 0:sendData=1;Log.i(TAG,"点击0");break;case 1:sendData=2;Log.i(TAG,"点击1");break;case 2:sendData=3;Log.i(TAG,"点击2");break;case 3:sendData=4;Log.i(TAG,"点击3");break;case 4:sendData=5;Log.i(TAG,"点击4");break;case 5:sendData=6;Log.i(TAG,"点击5");break;case 6:sendData=7;Log.i(TAG,"点击6");break;case 7:sendData=8;Log.i(TAG,"点击7");break;case 8:sendData=9;Log.i(TAG,"点击8");break;}}@Overridepublic void onNothingSelected(AdapterView<?> adapterView) {}});}//添加右边文本private void addRightItem(String text) {ChatListData date = new ChatListData();date.setType(ChatListAdapter.VALUE_RIGHT_TEXT);date.setText(text);mList.add(date);//通知adapter刷新adapter.notifyDataSetChanged();//滚动到底部mChatListView.setSelection(mChatListView.getBottom());}//添加左边文本private void addLeftItem(String text) {ChatListData date = new ChatListData();date.setType(ChatListAdapter.VALUE_LEFT_TEXT);date.setText(text);mList.add(date);//通知adapter刷新adapter.notifyDataSetChanged();//滚动到底部mChatListView.setSelection(mChatListView.getBottom());}public void btn_send(View v){writeStream(sendData);addRightItem(sendData+"");}@Overrideprotected void onStart() {super.onStart();initDevice();}@Overrideprotected void onStop() {close();super.onStop();}private void initDevice() {Bundle bundle = getIntent().getExtras();if (bundle != null) {mBluetoothDevice = bundle.getParcelable("device");if (mBluetoothDevice != null) {new Thread(mConnRun).start();}}}private Runnable mConnRun = new Runnable() {@Overridepublic void run() {connect();}};private void connect() {UUID uuid = UUID.fromString(SPP_UUID);try {mSocket = mBluetoothDevice.createRfcommSocketToServiceRecord(uuid);} catch (IOException e) {if (mSocket != null) {try {mSocket.close();} catch (IOException e1) {Log.e(TAG, e1.getMessage());}}}try {mSocket.connect();} catch (IOException e) {if (mSocket != null) {try {mSocket.close();} catch (IOException e1) {Log.e(TAG, e1.getMessage());}}}try {mOutS = mSocket.getOutputStream();input=mSocket.getInputStream();handler.sendEmptyMessage(CONNECT_SUCCED);} catch (IOException e) {if (mOutS != null) {try {mOutS.close();} catch (IOException e1) {Log.e(TAG, e.getMessage());}}if (mSocket != null) {try {mSocket.close();} catch (IOException e1) {Log.e(TAG, e.getMessage());}}}}private void close() {if (mOutS != null) {try {mOutS.close();} catch (IOException e) {Log.e(TAG, e.getMessage());}}if (mSocket != null) {try {mSocket.close();} catch (IOException e) {Log.e(TAG, e.getMessage());}}}private void writeStream(byte data) {try {if (mOutS != null) {mOutS.write(data);Log.i(TAG,"输出---->>>是:"+data);mOutS.flush();}} catch (IOException e) {runOnUiThread(new Runnable() {@Overridepublic void run() {Toaster.shortToastShow(MainActivity.this, "连接超时");MainActivity.this.finish();}});}}public Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 1:break;case  mRecieve_SUCCED:addLeftItem(mRecieve);break;case CONNECT_SUCCED:Log.i(TAG,"接收成功");new MyThread().start(); //开启下面的线程break;}}};//新开的一个接收的线程class MyThread extends Thread{@Overridepublic void run() {while (true){try {//获取到的数据int read = input.read();//因为不允许在子线程更新UI所以用handlerhandler.sendEmptyMessage(mRecieve_SUCCED);Log.i(TAG+"数据是",Integer.toHexString(read));} catch (IOException e) {e.printStackTrace();Log.i(TAG,"异常"+e);}}}}@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if(keyCode== KeyEvent.KEYCODE_BACK){AlertDialog.Builder alertDialog=new AlertDialog.Builder(MainActivity.this);alertDialog.setTitle("确认!");alertDialog.setMessage("        确定退出智能锁控制吗");alertDialog.setPositiveButton("否",new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface arg0, int arg1) {}});alertDialog.setNegativeButton("是", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface arg0, int arg1) {MainActivity.this.finish();}});alertDialog.show();}return false;}}

运行效果:


完美通讯~~

安卓通讯之《蓝牙单片机通讯助手》②扫描设备、连接设备和双向通讯。相关推荐

  1. 手机app和单片机蓝牙通讯c语言,单片机怎么和手机通信,你知道吗?

    原标题:单片机怎么和手机通信,你知道吗? 在用单片机做产品的时候,难免会用到单片机和手机通信,能和手机通信的方案有很多这种,像常用的蓝牙,Wifi等等,当然还有更高层次的通过互联网,一般我们使用比较多 ...

  2. 【TB-02模组专题③】微信小程序蓝牙通讯 Ble 蓝牙Mesh TB02模组;

    本<安信可ble mesh蓝牙模组TB-02模组专题>系列博客学习由官方博客 CSDN安信可博客 潜心所力所写.如有不对之处,请留言,我们及时更改. 1.BLE MESH开发环境linux ...

  3. 安卓手机与蓝牙模块联合调试(二)—— 单片机蓝牙控制LED灯亮灭(上)

    系列博文: (1)安卓手机与蓝牙模块联合调试(一)--蓝牙模块的串口通讯 (2)安卓手机与蓝牙模块联合调试(二)-- 单片机蓝牙控制LED灯亮灭(上) (3)安卓手机与蓝牙模块联合调试(三)-- 单片 ...

  4. 安卓手机与蓝牙模块联合调试(三)—— 单片机蓝牙控制LED灯亮灭(下)

    源码获取 https://gitcode.net/cjt-bluetooth/android-51mcu 系列博文: (1)安卓手机与蓝牙模块联合调试(一)--蓝牙模块的串口通讯 (2)安卓手机与蓝牙 ...

  5. USART HMI智能串口屏与单片机双向通讯

    目录 ·HMI串口屏介绍 ·HMI串口屏开发实操 ·准备 ·界面认识 ·写命令 ·下载 ·HMI串口屏与单片机(stm32)双向通信 [ 功能实现: STM32控制串口屏对应的数值.文本完成相应变化 ...

  6. 51单片机通过ESP8266模块与手机进行通讯

    51单片机通过ESP8266模块与手机进行通讯 WIFI 工作方式: 1.AP模式 提供热点 2.Station模式 作为设备接入热点 3.同时支持 配置 通过USB转TTL模块把ESP8266模块和 ...

  7. 蓝牙双向通讯【可自定义协议】SDK

    一.简介: 此文档主要是介绍蓝牙双向通讯sdk的调用方法以及蓝牙双向传输的核心功能逻辑,用户可以根据需要自定义协议进行蓝牙双向通讯 二.SDK接入步骤: 1: 增加sdk库依赖: a)libs文件夹下 ...

  8. java 蓝牙读取数据格式,单片机与安卓手机通过蓝牙串口模块利用JSON数据格式通信实例...

    原标题:单片机与安卓手机通过蓝牙串口模块利用JSON数据格式通信实例 JSON 指的是 Java 对象表示法(Java Object Notation),JSON 是轻量级的文本数据交换格式,JSON ...

  9. Android 硬件通讯之 蓝牙,USB,WIFI(一.蓝牙)

    蓝牙 蓝牙通讯分为:经典蓝牙与低功耗蓝牙 现在所说的蓝牙设备,大部分都是在说4.0设备,ble也特指4.0设备. 在4.0之前重要的版本有2.1版本-基本速率/增强数据率(BR/EDR)和3.0 高速 ...

最新文章

  1. 隔壁组Leader降级了!从不pua,亲自写代码,自己加班也不让下属加班!
  2. 利用SQL建立数据库对象
  3. 阿里nacos安装及使用指南
  4. 关于不过洋节的通知_蟠桃宫小学关于平安夜、圣诞节安全教育告家长通知书
  5. 设计新Xlator扩展GlusterFS[转]
  6. 进击的UI-------------------RAC
  7. 列顺序对SQL Server复合索引的影响
  8. 2007年下半年网工考试试题+分析+标准答案
  9. matlab读入stl文件,【源码】二进制立体光刻文件(STL)的MATLAB读取函数stlread
  10. 结合公司业务后,对极光推送的进一步思考
  11. 谷歌广告联盟(Google Adsense)通过网站获利功能在线创收
  12. 如何用计算机计算概率,概率统计计算
  13. 每一个赞扬背后都有一两个“慕名而来”,而每一个抱怨背后都有100个“弃你而去”。
  14. 为什么我们的代码难以维护(草稿)
  15. python怎么打印字典_Python中的字典
  16. php laravel 教程,Laravel框架学习之新手教程
  17. 山东科技大学计算机科学与技术学硕,2021年山东科技大学计算机科学与技术(081200)硕士研究生招生信息_考研招生计划和招生人数 - 学途吧...
  18. 都有哪些语言是跨平台的?
  19. sonique的插件Dee2
  20. ranklib java_[LTR] RankLib.jar 包介绍

热门文章

  1. [課程筆記] 強化學習(李弘毅) L1. Policy Gradient
  2. 软件工程之开发与测试对缺陷重现条件的常见误解
  3. 游戏公司的越冬样本:出海和精品化成为新的增长点?
  4. 关于达内培训的名企定制班
  5. Windows常用快捷键,及控制台cmd的常用命令
  6. oracle 去摸_Suphx论文翻译(一)
  7. Astah 建模软件安装
  8. 【diffusion】扩散模型详解!理论+代码
  9. 东财《社会学X》综合作业
  10. week3-day7 某人想将手中的一张面值100元的人民币换成10元、5元、2元和1元面值的票子。要求换正好40张,且每种票子至少一张。问:有几种换法?