Android App蓝牙的设备配对音频传输点对点通信的讲解及实战(附源码和演示 超详细)
Posted showswoller
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android App蓝牙的设备配对音频传输点对点通信的讲解及实战(附源码和演示 超详细)相关的知识,希望对你有一定的参考价值。
需要源码请点赞关注收藏后评论区留言私信~~~
一、蓝牙设备配对
android提供了蓝牙模块的管理工具,名叫BluetoothAdapter。下面是BluetoothAdapter类常用的方法说明:
getDefaultAdapter:获取默认的蓝牙适配器。
getState:获取蓝牙的开关状态。
enable:启用蓝牙功能。
disable:禁用蓝牙功能。
getBondedDevices:获取已配对的设备集合。
getRemoteDevice:根据设备地址获取远程的设备对象。
startDiscovery:开始搜索周围的蓝牙设备。
cancelDiscovery:取消搜索周围的蓝牙设备。
蓝牙配对实现步骤如下
(1)初始化蓝牙适配器
(2)启用蓝牙功能
(3)搜索周围的蓝牙设备
(4)与指定的蓝牙设备配对
首先会搜索附近的蓝牙并显示他们的相关信息
配对完成后各自手机上申请如下
代码如下
package com.example.iot;
import android.annotation.SuppressLint;
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.Looper;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.iot.adapter.BlueListAdapter;
import com.example.iot.bean.BlueDevice;
import com.example.iot.util.BluetoothUtil;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@SuppressLint("SetTextI18n")
public class BluetoothPairActivity extends AppCompatActivity implements
OnCheckedChangeListener, OnItemClickListener
private static final String TAG = "BluetoothPairActivity";
private CheckBox ck_bluetooth; // 声明一个复选框对象
private TextView tv_discovery; // 声明一个文本视图对象
private ListView lv_bluetooth; // 声明一个用于展示蓝牙设备的列表视图对象
private BluetoothAdapter mBluetooth; // 声明一个蓝牙适配器对象
private BlueListAdapter mListAdapter; // 声明一个蓝牙设备的列表适配器对象
private List<BlueDevice> mDeviceList = new ArrayList<>(); // 蓝牙设备列表
private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象
private int mOpenCode = 1; // 是否允许扫描蓝牙设备的选择对话框返回结果代码
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_pair);
initBluetooth(); // 初始化蓝牙适配器
ck_bluetooth = findViewById(R.id.ck_bluetooth);
tv_discovery = findViewById(R.id.tv_discovery);
lv_bluetooth = findViewById(R.id.lv_bluetooth);
ck_bluetooth.setOnCheckedChangeListener(this);
if (BluetoothUtil.getBlueToothStatus()) // 已经打开蓝牙
ck_bluetooth.setChecked(true);
initBlueDevice(); // 初始化蓝牙设备列表
// 初始化蓝牙适配器
private void initBluetooth()
// 获取系统默认的蓝牙适配器
mBluetooth = BluetoothAdapter.getDefaultAdapter();
if (mBluetooth == null)
Toast.makeText(this, "当前设备未找到蓝牙功能", Toast.LENGTH_SHORT).show();
finish(); // 关闭当前页面
// 初始化蓝牙设备列表
private void initBlueDevice()
mDeviceList.clear();
// 获取已经配对的蓝牙设备集合
Set<BluetoothDevice> bondedDevices = mBluetooth.getBondedDevices();
for (BluetoothDevice device : bondedDevices)
mDeviceList.add(new BlueDevice(device.getName(), device.getAddress(), device.getBondState()));
if (mListAdapter == null) // 首次打开页面,则创建一个新的蓝牙设备列表
mListAdapter = new BlueListAdapter(this, mDeviceList);
lv_bluetooth.setAdapter(mListAdapter);
lv_bluetooth.setOnItemClickListener(this);
else // 不是首次打开页面,则刷新蓝牙设备列表
mListAdapter.notifyDataSetChanged();
private Runnable mDiscoverable = new Runnable()
public void run()
// Android8.0要在已打开蓝牙功能时才会弹出下面的选择窗
if (BluetoothUtil.getBlueToothStatus()) // 已经打开蓝牙
// 弹出是否允许扫描蓝牙设备的选择对话框
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(intent, mOpenCode);
else
mHandler.postDelayed(this, 1000);
;
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
if (buttonView.getId() == R.id.ck_bluetooth)
if (isChecked) // 开启蓝牙功能
ck_bluetooth.setText("蓝牙开");
if (!BluetoothUtil.getBlueToothStatus()) // 还未打开蓝牙
BluetoothUtil.setBlueToothStatus(true); // 开启蓝牙功能
mHandler.post(mDiscoverable);
else // 关闭蓝牙功能
ck_bluetooth.setText("蓝牙关");
cancelDiscovery(); // 取消蓝牙设备的搜索
BluetoothUtil.setBlueToothStatus(false); // 关闭蓝牙功能
initBlueDevice(); // 初始化蓝牙设备列表
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == mOpenCode) // 来自允许蓝牙扫描的对话框
// 延迟50毫秒后启动蓝牙设备的刷新任务
mHandler.postDelayed(mRefresh, 50);
if (resultCode == RESULT_OK)
Toast.makeText(this, "允许本地蓝牙被附近的其他蓝牙设备发现",
Toast.LENGTH_SHORT).show();
else if (resultCode == RESULT_CANCELED)
Toast.makeText(this, "不允许蓝牙被附近的其他蓝牙设备发现",
Toast.LENGTH_SHORT).show();
// 定义一个刷新任务,每隔两秒刷新扫描到的蓝牙设备
private Runnable mRefresh = new Runnable()
@Override
public void run()
beginDiscovery(); // 开始扫描周围的蓝牙设备
// 延迟30秒后再次启动蓝牙设备的刷新任务
mHandler.postDelayed(this, 30*1000);
;
// 开始扫描周围的蓝牙设备
private void beginDiscovery()
// 如果当前不是正在搜索,则开始新的搜索任务
if (!mBluetooth.isDiscovering())
initBlueDevice(); // 初始化蓝牙设备列表
tv_discovery.setText("正在搜索蓝牙设备");
mBluetooth.startDiscovery(); // 开始扫描周围的蓝牙设备
// 取消蓝牙设备的搜索
private void cancelDiscovery()
mHandler.removeCallbacks(mRefresh);
tv_discovery.setText("取消搜索蓝牙设备");
// 当前正在搜索,则取消搜索任务
if (mBluetooth.isDiscovering())
mBluetooth.cancelDiscovery(); // 取消扫描周围的蓝牙设备
@Override
protected void onStart()
super.onStart();
mHandler.postDelayed(mRefresh, 50);
// 需要过滤多个动作,则调用IntentFilter对象的addAction添加新动作
IntentFilter discoveryFilter = new IntentFilter();
discoveryFilter.addAction(BluetoothDevice.ACTION_FOUND);
discoveryFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
discoveryFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
// 注册蓝牙设备搜索的广播接收器
registerReceiver(discoveryReceiver, discoveryFilter);
@Override
protected void onStop()
super.onStop();
cancelDiscovery(); // 取消蓝牙设备的搜索
unregisterReceiver(discoveryReceiver); // 注销蓝牙设备搜索的广播接收器
// 蓝牙设备的搜索结果通过广播返回
private BroadcastReceiver discoveryReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
String action = intent.getAction();
Log.d(TAG, "onReceive action=" + action);
// 获得已经搜索到的蓝牙设备
if (action.equals(BluetoothDevice.ACTION_FOUND)) // 发现新的蓝牙设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "name=" + device.getName() + ", state=" + device.getBondState());
refreshDevice(device, device.getBondState()); // 将发现的蓝牙设备加入到设备列表
else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) // 搜索完毕
//mHandler.removeCallbacks(mRefresh); // 需要持续搜索就要注释这行
tv_discovery.setText("蓝牙设备搜索完成");
else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) // 配对状态变更
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == BluetoothDevice.BOND_BONDING)
tv_discovery.setText("正在配对" + device.getName());
else if (device.getBondState() == BluetoothDevice.BOND_BONDED)
tv_discovery.setText("完成配对" + device.getName());
mHandler.postDelayed(mRefresh, 50);
else if (device.getBondState() == BluetoothDevice.BOND_NONE)
tv_discovery.setText("取消配对" + device.getName());
refreshDevice(device, device.getBondState()); // 刷新蓝牙设备列表
;
// 刷新蓝牙设备列表
private void refreshDevice(BluetoothDevice device, int state)
int i;
for (i = 0; i < mDeviceList.size(); i++)
BlueDevice item = mDeviceList.get(i);
if (item.address.equals(device.getAddress()))
item.state = state;
mDeviceList.set(i, item);
break;
if (i >= mDeviceList.size())
mDeviceList.add(new BlueDevice(device.getName(), device.getAddress(), device.getBondState()));
mListAdapter.notifyDataSetChanged();
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
//cancelDiscovery();
BlueDevice item = mDeviceList.get(position);
// 根据设备地址获得远端的蓝牙设备对象
BluetoothDevice device = mBluetooth.getRemoteDevice(item.address);
Log.d(TAG, "getBondState="+device.getBondState()+", item.state="+item.state);
if (device.getBondState() == BluetoothDevice.BOND_NONE) // 尚未配对
BluetoothUtil.createBond(device); // 创建配对信息
else if (device.getBondState() == BluetoothDevice.BOND_BONDED) // 已经配对
boolean isSucc = BluetoothUtil.removeBond(device); // 移除配对信息
if (!isSucc)
refreshDevice(device, BluetoothDevice.BOND_NONE); // 刷新蓝牙设备列表
二、蓝牙音频传输
通过蓝牙连接音箱,进而把手机上的音乐同步到音箱上播放,具体的编码过程主要有以下三个步骤:
(1)定义并设置A2DP的蓝牙代理(A2DP全称是蓝牙音频传输模型协定)
(2)发现蓝牙音箱,并进行配对和连接
(3)定义A2DP的广播接收器,并注册相关广播事件 蓝牙音箱连接成功,接下来播放手机音乐,就会由蓝牙音箱发出声音。
手机上开始播放放可以在音箱中听到音乐
代码如下
package com.example.iot;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothA2dp;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.example.iot.adapter.BlueListAdapter;
import com.example.iot.bean.BlueDevice;
import com.example.iot.util.BluetoothUtil;
import com.example.iot.widget.AudioPlayer;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@SuppressLint("SetTextI18n")
public class BluetoothA2dpActivity extends AppCompatActivity implements
OnCheckedChangeListener, OnItemClickListener
private static final String TAG = "BluetoothA2dpActivity";
private Context mContext; // 声明一个上下文对象
private CheckBox ck_bluetooth; // 声明一个复选框对象
private TextView tv_discovery; // 声明一个文本视图对象
private ListView lv_bluetooth; // 声明一个用于展示蓝牙设备的列表视图对象
private AudioPlayer ap_music; // 声明一个音频播放器对象
private BluetoothAdapter mBluetooth; // 声明一个蓝牙适配器对象
private BlueListAdapter mListAdapter; // 声明一个蓝牙设备的列表适配器对象
private List<BlueDevice> mDeviceList = new ArrayList<>(); // 蓝牙设备列表
private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象
private int mOpenCode = 1; // 是否允许扫描蓝牙设备的选择对话框返回结果代码
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_bluetooth_a2dp);
mContext = this;
initBluetooth(); // 初始化蓝牙适配器
ck_bluetooth = findViewById(R.id.ck_bluetooth);
tv_discovery = findViewById(R.id.tv_discovery);
lv_bluetooth = findViewById(R.id.lv_bluetooth);
ap_music = findViewById(R.id.ap_music);
ck_bluetooth.setOnCheckedChangeListener(this);
if (BluetoothUtil.getBlueToothStatus()) // 已经打开蓝牙
ck_bluetooth.setChecked(true);
initBlueDevice(); // 初始化蓝牙设备列表
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
Toast.makeText(this, "Android10开始无法调用A2DP的隐藏方法", Toast.LENGTH_SHORT).show();
// 初始化蓝牙适配器
private void initBluetooth()
// 获取系统默认的蓝牙适配器
mBluetooth = BluetoothAdapter.getDefaultAdapter();
if (mBluetooth == null)
Toast.makeText(this, "当前设备未找到蓝牙功能", Toast.LENGTH_SHORT).show();
finish(); // 关闭当前页面
// 初始化蓝牙设备列表
private void initBlueDevice()
mDeviceList.clear();
// 获取已经配对的蓝牙设备集合
Set<BluetoothDevice> bondedDevices = mBluetooth.getBondedDevices();
for (BluetoothDevice device : bondedDevices)
mDeviceList.add(new BlueDevice(device.getName(), device.getAddress(), device.getBondState()));
if (mListAdapter == null) // 首次打开页面,则创建一个新的蓝牙设备列表
mListAdapter = new BlueListAdapter(this, mDeviceList);
lv_bluetooth.setAdapter(mListAdapter);
lv_bluetooth.setOnItemClickListener(this);
else // 不是首次打开页面,则刷新蓝牙设备列表
mListAdapter.notifyDataSetChanged();
private Runnable mDiscoverable = new Runnable()
public void run()
// Android8.0要在已打开蓝牙功能时才会弹出下面的选择窗
if (BluetoothUtil.getBlueToothStatus()) // 已经打开蓝牙
// 弹出是否允许扫描蓝牙设备的选择对话框
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
startActivityForResult(intent, mOpenCode);
else
mHandler.postDelayed(this, 1000);
;
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
if (buttonView.getId() == R.id.ck_bluetooth)
if (isChecked) // 开启蓝牙功能
ck_bluetooth.setText("蓝牙开");
if (!BluetoothUtil.getBlueToothStatus()) // 还未打开蓝牙
BluetoothUtil.setBlueToothStatus(true); // 开启蓝牙功能
mHandler.post(mDiscoverable);
else // 关闭蓝牙功能
ck_bluetooth.setText("蓝牙关");
cancelDiscovery(); // 取消蓝牙设备的搜索
BluetoothUtil.setBlueToothStatus(false); // 关闭蓝牙功能
initBlueDevice(); // 初始化蓝牙设备列表
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent intent)
super.onActivityResult(requestCode, resultCode, intent);
if (requestCode == mOpenCode) // 来自允许蓝牙扫描的对话框
// 延迟50毫秒后启动蓝牙设备的刷新任务
mHandler.postDelayed(mRefresh, 50);
if (resultCode == RESULT_OK)
Toast.makeText(this, "允许本地蓝牙被附近的其他蓝牙设备发现",
Toast.LENGTH_SHORT).show();
else if (resultCode == RESULT_CANCELED)
Toast.makeText(this, "不允许蓝牙被附近的其他蓝牙设备发现",
Toast.LENGTH_SHORT).show();
// 定义一个刷新任务,每隔两秒刷新扫描到的蓝牙设备
private Runnable mRefresh = new Runnable()
@Override
public void run()
beginDiscovery(); // 开始扫描周围的蓝牙设备
// 延迟2秒后再次启动蓝牙设备的刷新任务
mHandler.postDelayed(this, 2000);
;
// 开始扫描周围的蓝牙设备
private void beginDiscovery()
// 如果当前不是正在搜索,则开始新的搜索任务
if (!mBluetooth.isDiscovering())
initBlueDevice(); // 初始化蓝牙设备列表
tv_discovery.setText("正在搜索蓝牙设备");
mBluetooth.startDiscovery(); // 开始扫描周围的蓝牙设备
// 取消蓝牙设备的搜索
private void cancelDiscovery()
mHandler.removeCallbacks(mRefresh);
tv_discovery.setText("取消搜索蓝牙设备");
// 当前正在搜索,则取消搜索任务
if (mBluetooth.isDiscovering())
mBluetooth.cancelDiscovery(); // 取消扫描周围的蓝牙设备
@Override
protected void onStart()
super.onStart();
mHandler.postDelayed(mRefresh, 50);
// 注册蓝牙设备搜索的广播接收器
IntentFilter discoveryFilter = new IntentFilter();
discoveryFilter.addAction(BluetoothDevice.ACTION_FOUND);
discoveryFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
discoveryFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(discoveryReceiver, discoveryFilter);
// 获取A2DP的蓝牙代理
mBluetooth.getProfileProxy(this, serviceListener, BluetoothProfile.A2DP);
IntentFilter a2dpFilter = new IntentFilter(); // 创建一个意图过滤器
// 指定A2DP的连接状态变更广播
a2dpFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
// 指定A2DP的播放状态变更广播
a2dpFilter.addAction(BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED);
registerReceiver(a2dpReceiver, a2dpFilter); // 注册A2DP连接管理的广播接收器
@Override
protected void onStop()
super.onStop();
cancelDiscovery(); // 取消蓝牙设备的搜索
unregisterReceiver(discoveryReceiver); // 注销蓝牙设备搜索的广播接收器
unregisterReceiver(a2dpReceiver); // 注销A2DP连接管理的广播接收器
// 蓝牙设备的搜索结果通过广播返回
private BroadcastReceiver discoveryReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
String action = intent.getAction();
Log.d(TAG, "onReceive action=" + action);
// 获得已经搜索到的蓝牙设备
if (action.equals(BluetoothDevice.ACTION_FOUND)) // 发现新的蓝牙设备
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
Log.d(TAG, "name=" + device.getName() + ", state=" + device.getBondState());
refreshDevice(device, device.getBondState()); // 将发现的蓝牙设备加入到设备列表
else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) // 搜索完毕
//mHandler.removeCallbacks(mRefresh); // 需要持续搜索就要注释这行
tv_discovery.setText("蓝牙设备搜索完成");
else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) // 配对状态变更
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() == BluetoothDevice.BOND_BONDING)
tv_discovery.setText("正在配对" + device.getName());
else if (device.getBondState() == BluetoothDevice.BOND_BONDED)
tv_discovery.setText("完成配对" + device.getName());
mHandler.postDelayed(mRefresh, 50);
else if (device.getBondState() == BluetoothDevice.BOND_NONE)
tv_discovery.setText("取消配对" + device.getName());
refreshDevice(device, device.getBondState()); // 刷新蓝牙设备列表
;
// 刷新蓝牙设备列表
private void refreshDevice(BluetoothDevice device, int state)
int i;
for (i = 0; i < mDeviceList.size(); i++)
BlueDevice item = mDeviceList.get(i);
if (item.address.equals(device.getAddress()))
item.state = state;
mDeviceList.set(i, item);
break;
if (i >= mDeviceList.size())
mDeviceList.add(new BlueDevice(device.getName(), device.getAddress(), device.getBondState()));
mListAdapter.notifyDataSetChanged();
private String mAddress;
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id)
mAddress = mDeviceList.get(position).address;
// 根据设备地址获得远端的蓝牙设备对象
BluetoothDevice device = mBluetooth.getRemoteDevice(mAddress);
if (device.getBondState() == BluetoothDevice.BOND_NONE) // 尚未配对
BluetoothUtil.connectA2dp(bluetoothA2dp, device); // 创建配对信息
else if (device.getBondState() == BluetoothDevice.BOND_BONDED) // 已经配对
BluetoothUtil.removeBond(device); // 移除配对信息
else if (device.getBondState() == BlueListAdapter.CONNECTED) // 已经建立A2DP连接
BluetoothUtil.disconnectA2dp(bluetoothA2dp, device); // 断开A2DP连接
private BluetoothA2dp bluetoothA2dp; // 声明一个蓝牙音频传输对象
// 定义一个A2DP的服务监听器,类似于Service的绑定方式启停,
// 也有onServiceConnected和onServiceDisconnected两个接口方法
private BluetoothProfile.ServiceListener serviceListener = new BluetoothProfile.ServiceListener()
// 在服务断开连接时触发
@Override
public void onServiceDisconnected(int profile)
if (profile == BluetoothProfile.A2DP)
Toast.makeText(mContext, "onServiceDisconnected", Toast.LENGTH_SHORT).show();
bluetoothA2dp = null; // A2DP已连接,则释放A2DP的蓝牙代理
// 在服务建立连接时触发
@Override
public void onServiceConnected(int profile, final BluetoothProfile proxy)
if (profile == BluetoothProfile.A2DP)
Toast.makeText(mContext, "onServiceConnected", Toast.LENGTH_SHORT).show();
bluetoothA2dp = (BluetoothA2dp) proxy; // A2DP已连接,则设置A2DP的蓝牙代理
;
// 定义一个A2DP连接的广播接收器
private BroadcastReceiver a2dpReceiver = new BroadcastReceiver()
@Override
public void onReceive(Context context, Intent intent)
switch (intent.getAction())
// 侦听到A2DP的连接状态变更广播
case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
BluetoothDevice device = mBluetooth.getRemoteDevice(mAddress);
int connectState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE,
BluetoothA2dp.STATE_DISCONNECTED);
if (connectState == BluetoothA2dp.STATE_CONNECTED)
// 收到连接上的广播,则更新设备状态为已连接
refreshDevice(device, BlueListAdapter.CONNECTED); // 刷新蓝牙设备列表
ap_music.initFromRaw(mContext, R.raw.mountain_and_water);
Toast.makeText(mContext, "已连上蓝牙音箱。快来播放音乐试试",
Toast.LENGTH_SHORT).show();
else if (connectState == BluetoothA2dp.STATE_DISCONNECTED)
// 收到断开连接的广播,则更新设备状态为已断开
refreshDevice(device, BluetoothDevice.BOND_NONE); // 刷新蓝牙设备列表
Toast.makeText(mContext, "已断开蓝牙音箱",
Toast.LENGTH_SHORT).show();
break;
// 侦听到A2DP的播放状态变更广播
case BluetoothA2dp.ACTION_PLAYING_STATE_CHANGED:
int playState = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE,
BluetoothA2dp.STATE_NOT_PLAYING);
if (playState == BluetoothA2dp.STATE_PLAYING)
Toast.makeText(mContext, "蓝牙音箱正在播放",
Toast.LENGTH_SHORT).show();
else if (playState == BluetoothA2dp.STATE_NOT_PLAYING)
Toast.makeText(mContext, "蓝牙音箱停止播放",
Toast.LENGTH_SHORT).show();
break;
;
三、点对点蓝牙通信
要想不通过网络服务器中转,直接把数据从A手机传给B手机,就要借助于蓝牙技术。 Android为蓝牙技术提供了4个工具类:
(1)蓝牙适配器BuletoothAdapter
(2)蓝牙设备BluetoothDevice
(3)蓝牙服务端套接字BluetoothServerSocket
(4)蓝牙客户端套接字BluetoothSocket
使用蓝牙传输数据的完整步骤
(1)开启蓝牙功能
(2)确认配对并完成绑定
(3)建立蓝牙连接
(4)通过蓝牙发送消息
运行测试效果如下
代码如下
package com.example.iot;
import androidx.appcompat.app.AppCompatActivity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import com.example.iot.constant.BleConstant;
import com.example.iot.util.BluetoothUtil;
import com.example.iot.util.ChatUtil;
import com.example.iot.util.DateUtil;
import com.example.iot.util.Utils;
import com.example.iot.util.ViewUtil;
import java.util.List;
public class BleServerActivity extends AppCompatActivity
private static final String TAG = "BleServerActivity";
private TextView tv_hint; // 声明一个文本视图对象
private ScrollView sv_chat; // 声明一个滚动视图对象
private LinearLayout ll_show; // 声明一个线性视图对象
private LinearLayout ll_input; // 声明一个线性视图对象
private EditText et_input; // 声明一个编辑框对象
private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象
private int dip_margin; // 每条聊天记录的四周空白距离
private String mMinute = "00:00";
private BluetoothManager mBluetoothManager; // 声明一个蓝牙管理器对象
private BluetoothAdapter mBluetoothAdapter; // 声明一个蓝牙适配器对象
private BluetoothDevice mRemoteDevice; // 声明一个蓝牙设备对象
private BluetoothGattServer mGattServer; // 声明一个蓝牙GATT服务器对象
private BluetoothGattCharacteristic mReadChara; // 客户端读取数据的特征值
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ble_server);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 保持屏幕常亮
initView(); // 初始化视图
initBluetooth(); // 初始化蓝牙适配器
mHandler.postDelayed(mAdvertise, 200); // 延迟200毫秒开启低功耗蓝牙广播任务
// 初始化视图
private void initView()
dip_margin = Utils.dip2px(this, 5);
tv_hint = findViewById(R.id.tv_hint);
sv_chat = findViewById(R.id.sv_chat);
ll_show = findViewById(R.id.ll_show);
ll_input = findViewById(R.id.ll_input);
et_input = findViewById(R.id.et_input);
findViewById(R.id.btn_send).setOnClickListener(v -> sendMesssage());
// 初始化蓝牙适配器
private void initBluetooth()
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE))
Toast.makeText(this, "当前设备不支持低功耗蓝牙", Toast.LENGTH_SHORT).show();
finish(); // 关闭当前页面
// 获取蓝牙管理器,并从中得到蓝牙适配器
mBluetoothManager =(BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter(); // 获取蓝牙适配器
if (!BluetoothUtil.getBlueToothStatus()) // 还未打开蓝牙
BluetoothUtil.setBlueToothStatus(true); // 开启蓝牙功能
// 创建一个低功耗蓝牙广播任务
private Runnable mAdvertise = new Runnable()
@Override
public void run()
if (BluetoothUtil.getBlueToothStatus()) // 已经打开蓝牙
stopAdvertise(); // 停止低功耗蓝牙广播
String server_name = getIntent().getStringExtra("server_name");
startAdvertise(server_name); // 开始低功耗蓝牙广播
tv_hint.setText("“"+server_name+"”服务端正在广播,请等候客户端连接");
else
mHandler.postDelayed(this, 2000);
;
// 开始低功耗蓝牙广播
private void startAdvertise(String ble_name)
// 设置广播参数
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setConnectable(true) // 是否允许连接
.setTimeout(0) // 设置超时时间
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.build();
// 设置广播内容
AdvertiseData advertiseData = new AdvertiseData.Builder()
.setIncludeDeviceName(true) // 是否把设备名称也广播出去
.setIncludeTxPowerLevel(true) // 是否把功率电平也广播出去
.build();
mBluetoothAdapter.setName(ble_name); // 设置BLE服务端的名称
// 获取BLE广播器
BluetoothLeAdvertiser advertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
// BLE服务端开始广播,好让别人发现自己
advertiser.startAdvertising(settings, advertiseData, mAdvertiseCallback);
// 停止低功耗蓝牙广播
private void stopAdvertise()
if (mBluetoothAdapter != null)
// 获取BLE广播器
BluetoothLeAdvertiser advertiser = mBluetoothAdapter.getBluetoothLeAdvertiser();
if (advertiser != null)
advertiser.stopAdvertising(mAdvertiseCallback); // 停止低功耗蓝牙广播
// 创建一个低功耗蓝牙广播回调对象
private AdvertiseCallback mAdvertiseCallback = new AdvertiseCallback()
@Override
public void onStartSuccess(AdvertiseSettings settings)
Log.d(TAG, "低功耗蓝牙广播成功:"+settings.toString());
addService(); // 添加读写服务UUID,特征值等
@Override
public void onStartFailure(int errorCode)
Log.d(TAG, "低功耗蓝牙广播失败,错误代码为"+errorCode);
;
// 添加读写服务UUID,特征值等
private void addService()
BluetoothGattService gattService = new BluetoothGattService(
BleConstant.UUID_SERVER, BluetoothGattService.SERVICE_TYPE_PRIMARY);
// 只读的特征值
mReadChara = new BluetoothGattCharacteristic(BleConstant.UUID_CHAR_READ,
BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_READ);
// 只写的特征值
BluetoothGattCharacteristic charaWrite = new BluetoothGattCharacteristic(BleConstant.UUID_CHAR_WRITE,
BluetoothGattCharacteristic.PROPERTY_WRITE | BluetoothGattCharacteristic.PROPERTY_NOTIFY,
BluetoothGattCharacteristic.PERMISSION_WRITE);
gattService.addCharacteristic(mReadChara); // 将特征值添加到服务里面
gattService.addCharacteristic(charaWrite); // 将特征值添加到服务里面
// 开启GATT服务器等待客户端连接
mGattServer = mBluetoothManager.openGattServer(this, mGattCallback);
mGattServer.addService(gattService); // 向GATT服务器添加指定服务
private BluetoothGattServerCallback mGattCallback = new BluetoothGattServerCallback()
// BLE连接的状态发生变化时回调
@Override
public void onConnectionStateChange(BluetoothDevice device, int status, int newState)
super.onConnectionStateChange(device, status, newState);
Log.d(TAG, "onConnectionStateChange device=" + device.toString() + " status=" + status + " newState=" + newState);
if (newState == BluetoothProfile.STATE_CONNECTED)
mRemoteDevice = device;
runOnUiThread(() ->
String desc = String.format("已连接BLE客户端,对方名称为“%s”,MAC地址为%s",
device.getName(), device.getAddress());
tv_hint.setText(desc);
ll_input.setVisibility(View.VISIBLE);
);
// 收到BLE客户端写入请求时回调
@Override
public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic chara, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value)
super.onCharacteristicWriteRequest(device, requestId, chara, preparedWrite, responseNeeded, offset, value);
String message = new String(value); // 把客户端发来的数据转成字符串
Log.d(TAG, "收到了客户端发过来的数据 " + message);
// 向GATT客户端发送应答,告诉它成功收到了要写入的数据
mGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, chara.getValue());
runOnUiThread(() -> appendChatMsg(message, false)); // 往聊天窗口添加聊天消息
;
@Override
protected void onDestroy()
super.onDestroy();
stopAdvertise(); // 停止低功耗蓝牙广播
if (mGattServer != null)
mGattServer.close(); // 关闭GATT服务器
// 发送聊天消息
private void sendMesssage()
String message = et_input.getText().toString();
if (TextUtils.isEmpty(message))
Toast.makeText(this, "请先输入聊天消息", Toast.LENGTH_SHORT).show();
return;
et_input.setText("");
ViewUtil.hideOneInputMethod(this, et_input); // 隐藏软键盘
List<String> msgList = ChatUtil.splitString(message, 20); // 按照20字节切片
for (String msg : msgList)
mReadChara.setValue(msg); // 设置读特征值
// 发送本地特征值已更新的通知
mGattServer.notifyCharacteristicChanged(mRemoteDevice, mReadChara, false);
appendChatMsg(message, true); // 往聊天窗口添加聊天消息
// 往聊天窗口添加聊天消息
private void appendChatMsg(String content, boolean isSelf)
appendNowMinute(); // 往聊天窗口添加当前时间
// 把单条消息的线性布局添加到聊天窗口上
ll_show.addView(ChatUtil.getChatView(this, content, isSelf));
// 延迟100毫秒后启动聊天窗口的滚动任务
new Handler(Looper.myLooper()).postDelayed(() ->
sv_chat.fullScroll(ScrollView.FOCUS_DOWN); // 滚动到底部
, 100);
// 往聊天窗口添加当前时间
private void appendNowMinute()
String nowMinute = DateUtil.getNowMinute();
if (!mMinute.substring(0, 4).equals(nowMinute.substring(0, 4)))
mMinute = nowMinute;
ll_show.addView(ChatUtil.getHintView(this, nowMinute, dip_margin));
创作不易 觉得有帮助请点赞关注收藏~~~
传统蓝牙学习记录:蓝牙的配对连接以及信息传输
一:简述
在上篇文章中,我们已经对传统蓝牙的开启以及周围设备的搜索有了一个初步的了解。
本篇文章将对蓝牙之间配对以及蓝牙的客户端和服务端建立连接的过程进行讲解。另外在上篇文章中虽然最后搜索到了周围的蓝牙设备,但并没有区分开来所有设备中的已配对和未配对的设备,这个缺陷将在本篇文章中改善。
二:搜索周围蓝牙设备(将已配对设备列表和未配对列表区分开来)
通过调用BluetoothAdapter的startDiscovery()方法即可开始扫描。
//点击搜索按钮,开始搜索周围设备 begin
mSearch.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v)
//如果正在扫描,则取消正在进行的扫描
if(bluetoothAdapter.isDiscovering())
bluetoothAdapter.cancelDiscovery();
bluetoothDevicesunpaired.clear(); //未配对设备列表
bluetoothDevicespaired.clear(); //已配对设备列表
bluetoothAdapter.startDiscovery();
);
switch (action)
case BluetoothDevice.ACTION_FOUND:
int state=device.getBondState();
if(state==BluetoothDevice.BOND_BONDED)
paired_adapter.addDev(device);
else
unpaired_adapter.addDev(device);
break;
二:蓝牙设备的配对
当本机搜索到周围设备之后,要先进行配对之后才能连接。先来看一下几个要用到的函数 :
BluetoothDevice.createBond(); //启动与远程设备的绑定(配对)过程。
BluetoothDevice.getBondState(); //获取远程设备的绑定状态。返回的值可能为: BOND_NONE,
BOND_BONDING, BOND_BONDED,分别表示未配对,正在配对中以
及已经配对这三种状态。
为未配对蓝牙列表设置点击事件,点击之后会开始配对,两台设备上都会弹出来请求配对的对话框,双方确认之后配对成功,之后该设备会从未配对列表转移到配对列表。
//为未配对蓝牙列表设置点击事件,我们暂定为点击一次之后,进行蓝牙配对
unpaired_adapter.setOnItemClickListener(new DeviceAdapter.OnItemClickListener()
@Override
public void onClick(int position)
Log.i("lcc check","the position is "+position);
bluetoothDevicesunpaired.get(position).createBond(); //创建绑定。执行之后会弹出来请求配对的对话框
);
case BluetoothDevice.ACTION_BOND_STATE_CHANGED:
int newState=device.getBondState();
if(newState==BluetoothDevice.BOND_BONDED)
Log.i("lcc check","the bond state is bonded");
unpaired_adapter.removeDev(device); //从未配对列表中删除
paired_adapter.addDev(device); //添加到已配对设备列表
Toast.makeText(MainActivity.this,"与设备"+device.getName()+"配对成功",Toast.LENGTH_LONG);
else if(newState==BluetoothDevice.BOND_BONDING)
Log.i("lcc check","the bond state is bonding");
else
Log.i("lcc check","the bond state is nobond");
unpaired_adapter.addDev(device);
paired_adapter.removeDev(device);
Toast.makeText(MainActivity.this,"与设备"+device.getName()+"配对失败",Toast.LENGTH_LONG);
break;
三:传统蓝牙的连接
蓝牙之间通过BluetoothSocket和BluetoothServerSocket通信。这和socket通信很相似.
两个蓝牙设备之间通信,必须有一个作为客户端,另一个作为服务器端。
//以下是API文档上给出的解释
The interface for Bluetooth Sockets is similar to that of TCP sockets: Socket
and ServerSocket
. On the server side, use a BluetoothServerSocket
to create a listening server socket. When a connection is accepted by the BluetoothServerSocket
, it will return a new BluetoothSocket
to manage the connection. On the client side, use a single BluetoothSocket
to both initiate an outgoing connection and to manage the connection.
The most common type of Bluetooth socket is RFCOMM, which is the type supported by the Android APIs. RFCOMM is a connection-oriented, streaming transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
在服务器端,使用BluetoothServerSocket
创建侦听服务器socket。当接受连接时BluetoothServerSocket
,它将返回一个新的BluetoothSocket
来管理连接。在客户端,使用单个BluetoothSocket
来启动传出连接和管理连接。最常见的蓝牙插座类型是RFCOMM,它是Android API支持的类型。RFCOMM是一种面向连接的蓝牙流媒体传输。它也称为串行端口配置文件(SPP)。
下面是客户端和服务器端创建连接的过程:(注:里面的SPP_UUID使我们自己定义的一个uuid号,创建RfcommSocket连接需要客户端和服务器使用确定的uuid号)。
//客户端
//为已配对蓝牙列表创建点击事件,暂且设置为:点击一次,与服务端创建连接 begin
paired_adapter.setOnItemClickListener(new DeviceAdapter.OnItemClickListener()
@Override
public void onClick(int position)
Log.i("lcc check","the position is "+position);
try
bluetoothSocket=bluetoothDevicespaired.get(position).createInsecureRfcommSocketToServiceRecord(SPP_UUID);
Log.i("lcc check","客户端和服务器尝试创建连接");
if(bluetoothSocket.isConnected())
//do nothing
Log.i("lcc check","连接已经建立,无需再进行连接");
else
bluetoothSocket.connect();
if(bluetoothSocket.isConnected())
Log.i("lcc check","客户端和服务器已连接");
Toast.makeText(MainActivity.this,"连接已经建立",Toast.LENGTH_LONG).show();
readThread=new ReadThread();
readThread.start();
/* Intent intent=new Intent(MainActivity.this,CommunicationActivity.class);
startActivity(intent);*/
catch (Exception e)
Log.i("lcc check","the connection has not created");
);
//为已配对蓝牙列表创建点击事件,暂且设置为:点击一次,与服务端创建连接 end
//服务器端
private class myThread extends Thread
@Override
public void run()
try
bluetoothServerSocket=bluetoothAdapter.listenUsingRfcommWithServiceRecord("lcc",SPP_UUID);
catch (Exception e)
e.printStackTrace();
Log.i("lcc check","等待连接...");
while(true)
try
socket=bluetoothServerSocket.accept();
BluetoothDevice device=socket.getRemoteDevice();
Log.i("lcc check","客户端名字为:"+device.getName()+device.getAddress());
if(socket.isConnected())
Log.i("lcc check","已经建立连接");
Message message=Message.obtain();
message.what=CONNECTED;
myhandler.sendMessage(message);
readThread=new ReadThread();
readThread.start();
else
Message message=Message.obtain();
message.what=DISCONNECTED;
myhandler.sendMessage(message);
catch (Exception e)
Log.i("lcc check","未能成功建立连接");
e.printStackTrace();
Message message=Message.obtain();
message.what=DISCONNECTED;
myhandler.sendMessage(message);
四:客户端和服务器之间通信(传递短消息)
当创建连接之后,两者之间就可以通过Bluetoothsocket收发消息了,代码如下:
//客户端发
//通过socket发送信息 begin
private void sendMessage(String msg)
if (bluetoothSocket == null)
//showToast("没有连接");
Toast.makeText(MainActivity.this,"没有连接",Toast.LENGTH_SHORT);
return;
try
Log.i("lcc check","the msg is "+msg);
//showToast("发出的指令是" + msg);
Toast.makeText(MainActivity.this,"客户端:"+msg,Toast.LENGTH_SHORT).show();
//输出流输出信息
OutputStream os = bluetoothSocket.getOutputStream();
os.write(msg.getBytes());
os.flush();
String info=String.format("\\n客户端:%s",msg);
communicationMsg.append(info);
catch (IOException e)
e.printStackTrace();
//通过socket发送信息 end
//服务器端收
private class ReadThread extends Thread
public void run()
byte[] buffer = new byte[1024];
int bytes;
InputStream is = null;
try
is = socket.getInputStream();
while (true)
if ((bytes = is.read(buffer)) > 0)
byte[] buf_data = new byte[bytes];
for (int i = 0; i < bytes; i++)
buf_data[i] = buffer[i];
String s = new String(buf_data);
Log.i("lcc check","the msg is "+s);
/*
在子线程里面修改组件等无效,会报错
String info=String.format("\\n客户端:%s",s);
communication.append(info);*/
Message msg=new Message();
msg.what=READMSG;
Bundle bundle=new Bundle();
bundle.putString("msg",s);
msg.setData(bundle);
myhandler.sendMessage(msg);
catch (IOException e1)
e1.printStackTrace();
finally
try
is.close();
catch (IOException e1)
e1.printStackTrace();
客户端的接收以及服务器端的发送也是类似。
以上是关于Android App蓝牙的设备配对音频传输点对点通信的讲解及实战(附源码和演示 超详细)的主要内容,如果未能解决你的问题,请参考以下文章
Android Developer -- Bluetooth篇 概述