设备:客户端:小米8 Android 9.0,服务端:小米5 Android8.0
蓝牙通信步骤:【搜索,配对,连接,通信】
目录
一、蓝牙搜索功能的实现
1.判断设备是否支持蓝牙,取得蓝牙适配器:bluetoothAdapter
2.打开蓝牙:
3.搜索蓝牙
二、蓝牙配对功能的实现
三、蓝牙连接功能的实现
四、蓝牙通信功能的实现
五、AndroidManifest.xml
private BluetoothAdapter bluetoothAdapter; //蓝牙适配器BluetoothAdapter
/**
* 判断是否设备是否支持蓝牙
* @return 是否支持
*/
private boolean isSupported(){
//初始化
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null){
Log.i(TAG, "isSupported: 设备不支持蓝牙");
return false;
}else{
return true;
}
}
1、判断蓝牙是否打开:bluetoothAdapter.isEnabled();
需要在 AndroidManifest.xml 中添加 android.permission.BLUETOOTH 权限
2、判断Android版本,现在版本高了需要在activity中添加动态权限,否则搜索时 无法发现蓝牙设备 。
也需在AndroidManifest.xml 添加 android.permission.ACCESS_FINE_LOCATION 和 ACCESS_COARSE_LOCATION权限
3、打开蓝牙设备:bluetoothAdapter.enable();
需要在 AndroidManifest.xml 中添加 android.permission.BLUETOOTH_ADMIN 权限
//判断蓝牙是否打开:bluetoothAdapter.isEnabled()
//Missing permissions required by BluetoothAdapter.isEnabled: android.permission.BLUETOOTH
//出现这个是因为没有在 AndroidManifest.xml 中添加权限
if(bluetoothAdapter.isEnabled()){
//已经打开蓝牙,判断Android版本是否需要添加权限,解决:无法发现蓝牙设备的问题
getPermission();
}else{
//开启蓝牙
bluetoothAdapter.enable();
//关闭蓝牙:bluetoothAdapter.disable();
}
/**
* 动态权限
*
* 解决:无法发现蓝牙设备的问题
*
* 对于发现新设备这个功能, 还需另外两个权限(Android M 以上版本需要显式获取授权,附授权代码):
*/
private final int ACCESS_LOCATION=1;
@SuppressLint("WrongConstant")
private void getPermission() {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
int permissionCheck = 0;
permissionCheck = this.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION);
permissionCheck += this.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION);
if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
//未获得权限
this.requestPermissions( // 请求授权
new String[]{Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION},
ACCESS_LOCATION);// 自定义常量,任意整型
}
}
}
/**
* 请求权限的结果回调。每次调用 requestpermissions(string[],int)时都会调用此方法。
* @param requestCode 传入的请求代码
* @param permissions 传入permissions的要求
* @param grantResults 相应权限的授予结果:PERMISSION_GRANTED 或 PERMISSION_DENIED
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch (requestCode) {
case ACCESS_LOCATION:
if (hasAllPermissionGranted(grantResults)) {
Log.i(TAG, "onRequestPermissionsResult: 用户允许权限");
} else {
Log.i(TAG, "onRequestPermissionsResult: 拒绝搜索设备权限");
}
break;
}
}
private boolean hasAllPermissionGranted(int[] grantResults) {
for (int grantResult : grantResults) {
if (grantResult == PackageManager.PERMISSION_DENIED) {
return false;
}
}
return true;
}
1、在当前activity的 onStart() 中注册广播
2、按钮点击进行搜索设备
3、写一个广播来接收查询到的设备数据
4、在当前activity的 onStop() 中取消广播
/**
* 1.在当前activity的 onStart() 中注册广播
*/
@Override
protected void onStart() {
super.onStart();
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);//创建一个查找蓝牙设备的广播意图
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);// 创建一个结束查找蓝牙设备结束的广播意图
//注册一个广播接收者,开启查找蓝牙设备意图后将结果以广播的形式返回
this.registerReceiver(mReceiver, filter);
}
/**
* 2. 按钮点击进行搜索设备的方法
*
* 搜索未配对的设备
*/
private void SearchDevices(){
//判断是否正在搜索
if (bluetoothAdapter.isDiscovering()) {
//如果正在搜索则取消搜索后再搜索
bluetoothAdapter.cancelDiscovery();
}
// 开始搜索蓝牙设备,搜索到的蓝牙设备通过广播接受者返回
boolean startDiscovery= bluetoothAdapter.startDiscovery();
if(startDiscovery){
//开始搜索:
// 清空之前的记录
devicesUnpaired.clear();
nameUnpaired.clear();
}
}
//存放未配对的蓝牙设备
ArrayList devicesUnpaired=new ArrayList<>();
//存放未配对的蓝牙设备的名称或地址【方便ListView中展示】
ArrayList nameUnpaired=new ArrayList<>();
/**
* 3. 写一个广播来接收查询到的设备数据
* BroadcastReceiver 广播接收者
* BluetoothDevice 蓝牙设备
*/
private final BroadcastReceiver mReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent){
String action = intent.getAction();
BluetoothDevice device;
switch (action) {
case BluetoothAdapter.ACTION_DISCOVERY_FINISHED:
Log.i(TAG, "ACTION_DISCOVERY_FINISHED: 搜索完毕");
showToast("搜索完毕");
break;
case BluetoothDevice.ACTION_FOUND:
Log.i(TAG, "ACTION_FOUND: 正在搜索设备...");
device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//搜索到的蓝牙设备【未配对+已配对】
//取得未配对设备集合
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
showToast(device.getName()==null?device.getAddress():device.getName());
devicesUnpaired.add(device);
nameUnpaired.add(device.getName()==null?device.getAddress():device.getName());
}
break;
}
}
};
/**
* 4. 取消注册广播
*/
@Override
protected void onStop() {
super.onStop();
this.unregisterReceiver(mReceiver);
}
该方法会弹出蓝牙配对确认框
//【手动配对】会弹出 配对确认框
private void unpairedDevicesList(){
//viewUnpaired:是listView。nameUnpaire:是之前取得的未配对的蓝牙名称。devicesUnpaired:是之前取得的未配对的蓝牙设备
ArrayAdapter adapter = new ArrayAdapter(
MainActivity.this, android.R.layout.simple_list_item_1, nameUnpaired);
viewUnpaired.setAdapter(adapter);
viewUnpaired.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
try {
BluetoothDevice device=devicesUnpaired.get((int) id);
// 调用配对的方法,此方法是异步的,系统会触发BluetoothDevice.ACTION_PAIRING_REQUEST的广播
// 收到此广播后,设置配对的密码
Method createBondMethod = device.getClass().getMethod("createBond");
createBondMethod.invoke(device);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
}
取的已配对的设备进行连接:
1、网上有个建立和管理蓝牙连接的工具类:BluetoothChatUtil.java
2、取得已配对设备列表,选择设备进行连接
3、在onCreate中获取实例BluetoothChatUtil,且注册Handler
4、写Handler
工具类:BluetoothChatUtil.java
/**
*该类的工作:建立和管理蓝牙连接。
*共有三个线程。mAcceptThread线程用来监听socket连接(服务端使用).
*mConnectThread线程用来连接serversocket(客户端使用)。
*mConnectedThread线程用来处理socket发送、接收数据。(客户端和服务端共用)
*/
public class BluetoothChatUtil {
private static final String TAG = "BluetoothChatClient";
private static final boolean D = true;
// 服务名 SDP
private static final String SERVICE_NAME = "BluetoothChat";
// uuid SDP
private static final UUID SERVICE_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// 蓝牙适配器
private final BluetoothAdapter mAdapter;
private Handler mHandler;
private AcceptThread mAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
private static BluetoothChatUtil mBluetoothChatUtil;
private BluetoothDevice mConnectedBluetoothDevice;
//常数,指示当前的连接状态
public static final int STATE_NONE = 0; // 当前没有可用的连接
public static final int STATE_LISTEN = 1; // 现在侦听传入的连接
public static final int STATE_CONNECTING = 2; // 现在开始连接
public static final int STATE_CONNECTED = 3; // 现在连接到远程设备
public static final int STATAE_CONNECT_FAILURE = 4; //连接失败
public static final int MESSAGE_DISCONNECTED = 5; //断开连接
public static final int STATE_CHANGE = 6; //连接状态改变
public static final int MESSAGE_READ = 7;
public static final int MESSAGE_WRITE= 8;
public static final String DEVICE_NAME = "device_name";
public static final String READ_MSG = "read_msg";
/**
* 构造函数。准备一个新的bluetoothchat会话。
* @param context
*/
private BluetoothChatUtil(Context context) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
}
public static BluetoothChatUtil getInstance(Context c){
if(null == mBluetoothChatUtil){
mBluetoothChatUtil = new BluetoothChatUtil(c);
}
return mBluetoothChatUtil;
}
public void registerHandler(Handler handler){
mHandler = handler;
}
public void unregisterHandler(){
mHandler = null;
}
/**
* 设置当前状态的聊天连接
* @param state 整数定义当前连接状态
*/
private synchronized void setState(int state) {
if (D) Log.d(TAG, "setState() " + mState + " -> " + state);
mState = state;
// 给新状态的处理程序,界面活性可以更新
mHandler.obtainMessage(STATE_CHANGE, state, -1).sendToTarget();
}
/**
* 返回当前的连接状态。 */
public synchronized int getState() {
return mState;
}
public BluetoothDevice getConnectedDevice(){
return mConnectedBluetoothDevice;
}
/**
* 开始聊天服务。特别acceptthread开始
* 开始服务器模式。 */
public synchronized void startListen() {
if (D) Log.d(TAG, "start");
// 取消任何线程正在运行的连接
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
// 启动线程来监听一个bluetoothserversocket
if (mAcceptThread == null) {
mAcceptThread = new AcceptThread();
mAcceptThread.start();
}
setState(STATE_LISTEN);
}
/**
* 开始connectthread启动连接到远程设备。
* @param device 连接的蓝牙设备
*/
public synchronized void connect(BluetoothDevice device) {
if (D) Log.d(TAG, "connect to: " + 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开始管理一个蓝牙连接,传输、接收数据.
* @param socket socket连接
* @param device 已连接的蓝牙设备
*/
public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
if (D) Log.d(TAG, "connected");
//取消任何线程正在运行的连接
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
// 启动线程管理连接和传输
mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();
//把连接设备的名字传到 ui Activity
mConnectedBluetoothDevice = device;
Message msg = mHandler.obtainMessage(STATE_CONNECTED);
Bundle bundle = new Bundle();
bundle.putString(DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
setState(STATE_CONNECTED);
}
/**
* 停止所有的线程
*/
public synchronized void disconnect() {
if (D) Log.d(TAG, "disconnect");
if (mConnectThread != null) {mConnectThread.cancel(); mConnectThread = null;}
if (mConnectedThread != null) {mConnectedThread.cancel(); mConnectedThread = null;}
if (mAcceptThread != null) {mAcceptThread.cancel(); mAcceptThread = null;}
setState(STATE_NONE);
}
/**
* Write to the ConnectedThread in an unsynchronized manner
* @param out The bytes to write
* @see ConnectedThread#write(byte[])
*/
public void write(byte[] out) {
//创建临时对象
ConnectedThread r;
// 同步副本的connectedthread
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
// 执行写同步
r.write(out);
}
/**
* Indicate that the connection attempt failed and notify the UI Activity.
*/
private void connectionFailed() {
// 发送失败的信息带回活动
Message msg = mHandler.obtainMessage(STATAE_CONNECT_FAILURE);
mHandler.sendMessage(msg);
mConnectedBluetoothDevice = null;
setState(STATE_NONE);
}
/**
* Indicate that the connection was lost and notify the UI Activity.
*/
public void connectionLost() {
// 发送失败的信息带回Activity
Message msg = mHandler.obtainMessage(MESSAGE_DISCONNECTED);
mHandler.sendMessage(msg);
mConnectedBluetoothDevice = null;
setState(STATE_NONE);
}
/**
*本线程 侦听传入的连接。
*它运行直到连接被接受(或取消)。
*/
private class AcceptThread extends Thread {
// 本地服务器套接字
private final BluetoothServerSocket mServerSocket;
public AcceptThread() {
BluetoothServerSocket tmp = null;
// 创建一个新的侦听服务器套接字
try {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(
SERVICE_NAME, SERVICE_UUID);
//tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(SERVICE_NAME, SERVICE_UUID);
} catch (IOException e) {
Log.e(TAG, "listen() failed", e);
}
mServerSocket = tmp;
}
public void run() {
if (D) Log.d(TAG, "BEGIN mAcceptThread" + this);
setName("AcceptThread");
BluetoothSocket socket = null;
// 循环,直到连接成功
while (mState != STATE_CONNECTED) {
try {
// 这是一个阻塞调用 返回成功的连接
// mServerSocket.close()在另一个线程中调用,可以中止该阻塞
socket = mServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "accept() failed", e);
break;
}
// 如果连接被接受
if (socket != null) {
synchronized (BluetoothChatUtil.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
// 正常情况。启动ConnectedThread。
connected(socket, socket.getRemoteDevice());
break;
case STATE_NONE:
case STATE_CONNECTED:
// 没有准备或已连接。新连接终止。
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}
}
if (D) Log.i(TAG, "END mAcceptThread");
}
public void cancel() {
if (D) Log.d(TAG, "cancel " + this);
try {
mServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of server failed", e);
}
}
}
/**
* 本线程用来连接设备
*
*/
private class ConnectThread extends Thread {
private BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
// 得到一个bluetoothsocket
try {
mmSocket = device.createRfcommSocketToServiceRecord
(SERVICE_UUID);
} catch (IOException e) {
Log.e(TAG, "create() failed", e);
mmSocket = null;
}
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread");
try {
// socket 连接,该调用会阻塞,直到连接成功或失败
mmSocket.connect();
} catch (IOException e) {
Log.e(TAG, "IOException");
connectionFailed();
try {//关闭这个socket
mmSocket.close();
} catch (IOException e2) {
e2.printStackTrace();
}
return;
}
// 启动连接线程
connected(mmSocket, mmDevice);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
/**
* 本线程server 和client共用.
* 它处理所有传入和传出的数据。
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket) {
Log.d(TAG, "create ConnectedThread");
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
// 获得bluetoothsocket输入输出流
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "没有创建临时sockets", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
// 监听输入流
while (true) {
try {
byte[] buffer = new byte[512];
// 读取输入流
int bytes = mmInStream.read(buffer);
// 发送获得的字节的ui activity
Message msg = mHandler.obtainMessage(MESSAGE_READ);
Bundle bundle = new Bundle();
bundle.putByteArray(READ_MSG, buffer);
msg.setData(bundle);
mHandler.sendMessage(msg);
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
break;
}
}
}
/**
* 向外发送。
* @param buffer 发送的数据
*/
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
// 分享发送的信息到Activity
mHandler.obtainMessage(MESSAGE_WRITE, -1, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
public static int ByteArrayToInt(byte b[]) throws Exception {
ByteArrayInputStream buf = new ByteArrayInputStream(b);
DataInputStream dis = new DataInputStream(buf);
return dis.readInt();
}
}
【客户端】
private BluetoothChatUtil mBlthChatUtil;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBlthChatUtil = BluetoothChatUtil.getInstance(this);
mBlthChatUtil.registerHandler(mHandler);
}
//存放已配对的蓝牙设备
ArrayList devicesPaired=new ArrayList<>();
/**
* 我这的方法是:
* 取得已经配对的蓝牙设备的 名称或者地址 显示在listView中
* 且listView的选项设置点击事件,以便进行设备连接
*/
private void PairedDevicesList(){
//已配对设备集合
Set pairedDevices = bluetoothAdapter.getBondedDevices();
//listView需要string类型,所以提取名称或者地址以方便展示
ArrayList mArrayAdapter = new ArrayList<>();
if(pairedDevices.size() > 0){
//如果有配对的设备
for(BluetoothDevice device : pairedDevices){
//通过array adapter在列表中添加设备名称和地址
if(device.getName()==null){
mArrayAdapter.add(device.getAddress());
}else{
mArrayAdapter.add(device.getName());
}
devicesPaired.add(device);
}
}else{
mArrayAdapter.add("暂无已配对设备");
}
//将已配对的设备添加到listView中
ArrayAdapter adapter = new ArrayAdapter(
MainActivity.this, android.R.layout.simple_list_item_1, mArrayAdapter);
viewPaired.setAdapter(adapter);
viewPaired.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
BluetoothDevice device=devicesPaired.get((int) id);
if (bluetoothAdapter.isDiscovering()) {
//取消搜索
bluetoothAdapter.cancelDiscovery();
}
if (mBlthChatUtil.getState() == BluetoothChatUtil.STATE_CONNECTED) {
Toast.makeText(MainActivity.this, "蓝牙已连接!!!!!!!", Toast.LENGTH_SHORT).show();
}else {
mBlthChatUtil.connect(device);
}
}
});
}
private Handler mHandler = new Handler(){
public void handleMessage(Message msg) {
String deviceName = msg.getData().getString(BluetoothChatUtil.DEVICE_NAME);
switch(msg.what){
case BluetoothChatUtil.STATE_CONNECTED:
showToast("连接成功");
break;
case BluetoothChatUtil.STATAE_CONNECT_FAILURE:
showToast("连接失败");
break;
case BluetoothChatUtil.STATE_CHANGE:
showToast("正在连接设备..");
break;
case BluetoothChatUtil.MESSAGE_DISCONNECTED:
showToast("与设备断开连接");
break;
//读到另一方传送的消息
case BluetoothChatUtil.MESSAGE_READ:{
byte[] buf = msg.getData().getByteArray(BluetoothChatUtil.READ_MSG);
String str = new String(buf,0,buf.length);
Toast.makeText(getApplicationContext(), "读成功" + str, Toast.LENGTH_SHORT).show();
break;
}
//发送消息给另一方
case BluetoothChatUtil.MESSAGE_WRITE:{
byte[] buf = (byte[]) msg.obj;
String str = new String(buf,0,buf.length);
Toast.makeText(getApplicationContext(), "发送成功" + str, Toast.LENGTH_SHORT).show();
break;
}
default:
break;
}
};
};
//toast提示
public void showToast(String msg){
Toast.makeText(this,msg,Toast.LENGTH_SHORT).show();
}
【服务端】:就比客户端多了一项启动监听
/**
* 活动和用户进行交互时调用【服务端】
*/
@Override
protected void onResume() {
super.onResume();
if (mBlthChatUtil != null) {
if (mBlthChatUtil.getState() == BluetoothChatUtil.STATE_NONE) {
// 启动蓝牙聊天服务
mBlthChatUtil.startListen();
}else if (mBlthChatUtil.getState() == BluetoothChatUtil.STATE_CONNECTED){
BluetoothDevice device = mBlthChatUtil.getConnectedDevice();
if(null != device && null != device.getName()){
showToast("已成功连接到设备" + device.getName());
}else {;
showToast("未连接到设备");
}
}
}
}
//调用write写需要发送的数据就行
mBlthChatUtil.write("openIR#".getBytes());
//在mHandler方法中,接收方可对数据进行处理
好了简单记录一下
Dome源码:https://github.com/van9420/android-BluetoothDome