Android开发——蓝牙通信实现

  • 项目需求
  • 项目主要代码及功能实现
    • AndroidManifest.xml
    • res/values/string.xml
    • activity_main.xml
    • ChatService
    • res/menu/option_menu.xml
    • device_list.xml
    • DeviceList
    • BlueToothChat
  • 项目结果展示
  • 源码地址

项目需求

运用蓝牙进程通信,实现两个手机的蓝牙进行通信聊天。

项目主要代码及功能实现

AndroidManifest.xml

给予项目蓝牙通信的相关权限

    <!-- 下面2个是普通权限,只需要在清单文件里注册,不需要在程序里动态申请 --><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" /><uses-permission android:name="android.permission.BLUETOOTH_CONNECT" /><uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

res/values/string.xml

添加程序运行过程中的状态描述文本及配色代码等

<?xml version="1.0" encoding="utf-8"?>
<resources><string name="app_name">蓝牙Demo</string> <string name="send">发送</string><string name="not_connected">你没有链接一个设备</string><string name="bt_not_enabled_leaving">蓝牙不可用,离开聊天室</string><string name="title_connecting">链接中...</string><string name="title_connected_to">连接到:</string><string name="title_not_connected">无链接</string> <string name="scanning">蓝牙设备搜索中...</string><string name="select_device">选择一个好友链接</string><string name="none_paired">没有配对好友</string><string name="none_found">附近没有发现好友</string><string name="title_paired_devices">已配对好友</string><string name="title_other_devices">其它可连接好友</string><string name="button_scan">搜索好友</string><string name="connect">我的好友</string><string name="discoverable">设置在线</string><string name="back">退出</string><string name="startVideo">开始聊天</string><string name="stopVideo">结束聊天</string>
</resources>

activity_main.xml

添加1个Toolbar控件,其内包含2个水平的TextView控件;在Toolbar控件的下方添加1个ListView控件,用于显示聊天内容;最后在ListView控件的下方添加水平放置的1个EditText控件和一个Button控件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#E6CAFF">tools:context=".MainActivity"><!--新版Android支持的Toolbar,对标题栏布局--><androidx.appcompat.widget.Toolbarandroid:id="@+id/toolbar"android:layout_width="match_parent"android:layout_height="wrap_content"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="horizontal"><TextViewandroid:id="@+id/title_left_text"style="?android:attr/windowTitleStyle"android:layout_width="0dp"android:layout_height="match_parent"android:layout_alignParentLeft="true"android:layout_weight="1"android:gravity="left"android:ellipsize="end"android:singleLine="true" /><TextViewandroid:id="@+id/title_right_text"android:layout_width="0dp"android:layout_height="match_parent"android:layout_alignParentRight="true"android:layout_weight="1"android:ellipsize="end"android:gravity="right"android:singleLine="true"android:textColor="#fff" /></LinearLayout></androidx.appcompat.widget.Toolbar><ListView android:id="@+id/in"android:layout_width="match_parent"android:layout_height="match_parent"android:stackFromBottom="true"android:transcriptMode="alwaysScroll"android:layout_weight="1" /><LinearLayoutandroid:orientation="horizontal"android:layout_width="match_parent"android:layout_height="wrap_content" ><EditTextandroid:id="@+id/edit_text_out"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="bottom"android:layout_weight="1"tools:ignore="SpeakableTextPresentCheck" /><Button android:id="@+id/button_send"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@string/send"/></LinearLayout></LinearLayout>

ChatService

创建一个ChatService的java文件,作为工具类
部分代码如下:
构建蓝牙对象

//构造方法,接收UI主线程传递的对象public ChatService(Context context, Handler handler) {//构造方法完成蓝牙对象的创建mAdapter = BluetoothAdapter.getDefaultAdapter();mState = STATE_NONE;mHandler = handler;}

取消 CONNECTING 和 CONNECTED 状态下的相关线程,然后运行新的 mConnectThread 线程

    public synchronized void connect(BluetoothDevice device) {if (mState == STATE_CONNECTING) {if (mConnectThread != null) {mConnectThread.cancel();mConnectThread = null;}}if (mConnectedThread != null) {mConnectedThread.cancel();mConnectedThread = null;}mConnectThread = new ConnectThread(device);mConnectThread.start();setState(STATE_CONNECTING);}

开启一个 ConnectedThread 来管理对应的当前连接。之前先取消任意现存的 mConnectThread 、mConnectedThread 、 mAcceptThread 线程,然后开启新 mConnectedThread ,传入当前刚刚接受的socket 连接。最后通过 Handler来通知UI连接

    public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {if (mConnectThread != null) {mConnectThread.cancel();mConnectThread = null;}if (mConnectedThread != null) {mConnectedThread.cancel();mConnectedThread = null;}if (mAcceptThread != null) {mAcceptThread.cancel();mAcceptThread = null;}mConnectedThread = new ConnectedThread(socket);mConnectedThread.start();Message msg = mHandler.obtainMessage(BluetoothChat.MESSAGE_DEVICE_NAME);Bundle bundle = new Bundle();bundle.putString(BluetoothChat.DEVICE_NAME, device.getName());msg.setData(bundle);mHandler.sendMessage(msg);setState(STATE_CONNECTED);}

连接线程,专门用来对外发出连接对方蓝牙的请求和处理流程。构造函数里通过 BluetoothDevice.createRfcommSocketToServiceRecord() ,从待连接的 device 产生 BluetoothSocket. 然后在 run 方法中 connect ,成功后调用 BluetoothChatSevice 的 connected() 方法。定义 cancel() 在关闭线程时能够关闭相关socket 。

private class ConnectThread extends Thread {private final BluetoothSocket mmSocket;private final BluetoothDevice mmDevice;public ConnectThread(BluetoothDevice device) {mmDevice = device;BluetoothSocket tmp = null;try {tmp = device.createRfcommSocketToServiceRecord(MY_UUID);} catch (IOException e) {e.printStackTrace();}mmSocket = tmp;}@Overridepublic void run() {setName("ConnectThread");mAdapter.cancelDiscovery();try {mmSocket.connect();} catch (IOException e) {connectionFailed();try {mmSocket.close();} catch (IOException e2) {e.printStackTrace();}ChatService.this.start();return;}synchronized (ChatService.this) {mConnectThread = null;}connected(mmSocket, mmDevice);}public void cancel() {try {mmSocket.close();} catch (IOException e) {e.printStackTrace();}}}

res/menu/option_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"><item android:id="@+id/scan"android:icon="@android:drawable/ic_menu_myplaces"android:title="@string/connect" /><item android:id="@+id/discoverable"android:icon="@android:drawable/ic_menu_view"android:title="@string/discoverable" /><item android:id="@+id/back"android:icon="@android:drawable/ic_menu_close_clear_cancel"android:title="@string/back" />
</menu>

device_list.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><TextView android:id="@+id/title_paired_devices"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/title_paired_devices"android:visibility="gone"android:background="#666"android:textColor="#fff"android:paddingLeft="5dp" /><ListView android:id="@+id/paired_devices"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="1" /><TextView android:id="@+id/title_new_devices"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/title_other_devices"android:visibility="gone"android:background="#666"android:textColor="#fff"android:paddingLeft="5dp" /><!--android:visibility="gone"表示不占空间的隐藏,invisible是占空间--><ListView android:id="@+id/new_devices"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_weight="2" /><Button android:id="@+id/button_scan"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="@string/button_scan" />
</LinearLayout>

DeviceList

新建一个名为DeviceList的activity文件
本程序供菜单项主界面的选项菜单“我的友好”调用,用于:

  1. 显示已配对的好友列表;
  2. 搜索可配对的好友进行配对
  3. 新选择并配对的蓝牙设备将刷新好友列表
  4. 注意:发现新的蓝牙设备并请求配对时,需要对应接受
  5. 关键技术:动态注册一个广播接收者,处理蓝牙设备扫描的结果
    部分代码如下:
    定义广播接收者,用于处理扫描蓝牙设备后的结果
    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();if (BluetoothDevice.ACTION_FOUND.equals(action)) {BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);if (device.getBondState() != BluetoothDevice.BOND_BONDED) {mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());}} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {if (mNewDevicesArrayAdapter.getCount() == 0) {String noDevices = getResources().getText(R.string.none_found).toString();mNewDevicesArrayAdapter.add(noDevices);}}}};

活动界面:

private void init() {Button scanButton = findViewById(R.id.button_scan);scanButton.setOnClickListener(new View.OnClickListener() {public void onClick(View v) {Toast.makeText(DeviceList.this, R.string.scanning, Toast.LENGTH_LONG).show();doDiscovery();  //搜索蓝牙设备}});mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);//已配对蓝牙设备列表ListView pairedListView =findViewById(R.id.paired_devices);pairedListView.setAdapter(mPairedDevicesArrayAdapter);pairedListView.setOnItemClickListener(mPaireDeviceClickListener);//未配对蓝牙设备列表ListView newDevicesListView = findViewById(R.id.new_devices);newDevicesListView.setAdapter(mNewDevicesArrayAdapter);newDevicesListView.setOnItemClickListener(mNewDeviceClickListener);//动态注册广播接收者IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);registerReceiver(mReceiver, filter);filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);registerReceiver(mReceiver, filter);mBtAdapter = BluetoothAdapter.getDefaultAdapter();Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();if (pairedDevices.size() > 0) {findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);for (BluetoothDevice device : pairedDevices) {mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());}} else {String noDevices = getResources().getText(R.string.none_paired).toString();mPairedDevicesArrayAdapter.add(noDevices);}}

BlueToothChat

将MainActivity重命名为BlueToothChat
检查蓝牙权限

    public void checkBlePermission() {if (ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION)!= PackageManager.PERMISSION_GRANTED) {ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},1);} else {Log.i("tag","已申请权限");}}

判断是否支持蓝牙,并打开蓝牙,获取到BluetoothAdapter之后,还需要判断是否支持蓝牙,以及蓝牙是否打开。如果没打开,需要让用户打开蓝牙:

private void checkBleDevice() {//首先获取BluetoothManagerBluetoothManager bluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);//获取BluetoothAdapterif (bluetoothManager != null) {BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();if (mBluetoothAdapter != null) {if (!mBluetoothAdapter.isEnabled()) {//调用enable()方法直接打开蓝牙if (!mBluetoothAdapter.enable()){Log.i("tag","蓝牙打开失败");}else{Log.i("tag","蓝牙已打开");}//该方法也可以打开蓝牙,但是会有一个很丑的弹窗,可以自行尝试一下
//                    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
//                    enableBtIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//                    startActivity(enableBtIntent);}} else {Log.i("tag","同意申请");}}}

返回进入好友列表操作后的数回调方法

public void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);switch (requestCode) {case REQUEST_CONNECT_DEVICE:if (resultCode == Activity.RESULT_OK) {String address = data.getExtras().getString(DeviceList.EXTRA_DEVICE_ADDRESS);BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);mChatService.connect(device);} else if (resultCode == Activity.RESULT_CANCELED) {Toast.makeText(this, "未选择任何好友!", Toast.LENGTH_SHORT).show();}break;case REQUEST_ENABLE_BT:if (resultCode == Activity.RESULT_OK) {setupChat();} else {Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();finish();}}}

项目结果展示




源码地址

https://gitee.com/liu_peilin/bluetooth-communication

Android开发——蓝牙通信实现相关推荐

  1. android开发蓝牙传输图片,如何发送/接收文本和图片通过蓝牙android到另一个android手机...

    我假设你已经在开发Android的蓝牙应用的经验. Android蓝牙API不直接支持OBEX,意味着我无法将任何文件直接推送到任何设备. Android蓝牙API提供诸如发现,连接和使用流的数据传输 ...

  2. Android 开发:通信篇-TCP客户端

    这节教给大家用Android写一个TCP客户端程序 官方文档 Socket - Android SDK | Android Developers 页面 编写连接程序 1.获取控件 EditText e ...

  3. Android Studio 蓝牙通信BlueTooth

    BlueTooth蓝牙通信 作业内容 配置蓝牙使用权限 页面布局 Layout文件夹中 Values文件夹中 Menu文件夹中 事件监听控制 服务组件ChatService.java weixinFr ...

  4. android开发蓝牙是否可见开关_如何从后台开启android蓝牙的可见性以及始终保持可见性...

    最近工作中遇到一个特殊的需求,要求代码能够从后台开机android手机蓝牙的可见性.而framework提供了一种打开可见性的操作,就是通过向用户弹出一个提示框,来询问是否允许开启可见性.而且限制了最 ...

  5. Android开发蓝牙与ble设备的通讯

    一.写在前面的话 一直想写一篇关于蓝牙与ble设备通讯的博客,但是一直也不知道从何下手,可能是之前思路不清晰吧,也就一直拖拖拖,拖到现在.最近又做到关于ble设备的项目了,在此总结一下吧.(如有不到位 ...

  6. Android开发串口通信之开发板的串口通信

    本人最近刚开始学习android,学习大概将近一个月.学着学着突然想做个小东西出来.因为android前面的学习主要是UI 界面的学习,就想着做一个通信的串口 来实现app与外部的数据传输.通过界面的 ...

  7. android开发蓝牙自动连接电脑上,android – 配对设备的蓝牙自动连接

    我是新来的,我已经阅读了很多你的帖子,但仍然没有找到问题的解决方案. 我正在为Android 2.2编写一个使用蓝牙连接到终端设备的应用程序. 我有一个配对设备列表,我可以将我的Android平板电脑 ...

  8. Android开发--蓝牙操作

    首先,由于模拟器上没有蓝牙装置,所以我们需要一个含有蓝牙装置的Android系统 其次,要操作设备上的蓝牙装置,需要在AndroidManifest中声明两个权限: <uses-permissi ...

  9. Android开发-蓝牙遥控器(字符串形式发送)-应用例程

    一.例程简介 1.应用界面图(主界面.设置界面)   2.实现功能: (1)打开应用,显示主界面,检测蓝牙功能是否打开,否则询问打开: (2)打开蓝牙功能后,点击"连接设备:"下的 ...

最新文章

  1. spring 基于XML的申明式AspectJ通知的执行顺序
  2. 裁剪图像周围空白区域_零基础PS纠正倾斜的图像效果
  3. 【深度学习】围观特斯拉总监把玩MNIST
  4. linux中sudo如何读取标准输入作为密码,避免每次都输入密码?
  5. leetcode 494. Target Sum | 494. 目标和(动态规划)
  6. 在Linux服务器上配置phpMyAdmin
  7. 2G,3G ,4G 到 5G 变了什么 ?
  8. 百度AI学习:三、文字识别
  9. 【Clickhouse】Clickhouse 外部存储引擎之 File
  10. 前端 鼠标一次移动半个像素_今天来说说鼠标的DPI该怎么设置
  11. mysql jdbc配置重连_Spring Boot 配置MySQL数据库重连的操作方法
  12. SQLServer · 特性分析 · SQL Server 2012的分析函数未必都理解透了(2)
  13. gamit数据处理—5.遇到的问题
  14. 计算机符号的名字,符号网名大全
  15. 【lizhi125】分区助手:分区魔术师的终结者/无损分区魔术师!(免费中文完美支持Win7/32与64位系统)
  16. 测试面试题集锦(六)| 软素质篇与反问面试官篇(附答案)
  17. 医学影像分割论文合集
  18. 一、酷狗 歌词搜索 Indy TIdhttp
  19. py语法08-函数返回参数
  20. MySQL Java JDBC

热门文章

  1. 微信小程序在wxml页面中截取字符串
  2. 如何用PHP实现上传图片功能
  3. SAP EWM 在仓库管理监视器中配置自定义功能按钮
  4. 2019智能手表推荐_2019年,这4款智能手表已经秒杀Apple Watch!
  5. GCC最新版安装及错误解决
  6. github有什么作用_什么是GitHub?它的用途是什么?
  7. 普源DS1072U示波器70MHz带宽,500MSa/s采样率
  8. 网速正常服务器正常游戏延迟,网速时延多少正常(网络延迟与网速有关吗)
  9. 微信小程序搜索功能!附:小程序前端+PHP后端 1
  10. llowpoly风格人物模型(含骨骼动作提供下载)