去年公司的项目需要用到蓝牙开发的相关内容,因此查阅了Google官方文档的内容并进行二次整理。本文只涉及经典蓝牙(Classic Bluetooth)的开发,并不涉及低功耗蓝牙(BLE)的开发,本文应用场景为蓝牙单聊。
官方项目:GoogleSamples-android-BluetoothChat
Bluetooth Low Energy:学习笔记之低功耗蓝牙开发
效果图A-小米4手机
效果图B-魅蓝2手机
经典蓝牙需要用到2个权限
允许程序连接到已配对的蓝牙设备
允许程序发现和配对蓝牙设备
在AndroidManifest加上这个2个权限
(1)判断该设备是否支持蓝牙、蓝牙功能是否被打开
public boolean openBluetooth() {
if (adapter == null) {
Toast.makeText(this, "该设备不支持蓝牙", Toast.LENGTH_LONG).show();
return false;
} else {
if (!adapter.isEnabled()) { // 打开蓝牙
// 设置蓝牙可见性,最多300秒
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
intent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(intent, 0);
return false;
}
}
return true;
}
(2)在onActivityResult方法处理回调结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (resultCode) {
case RESULT_OK:
Toast.makeText(this, "开启蓝牙成功", Toast.LENGTH_SHORT).show();
break;
case RESULT_CANCELED:
Toast.makeText(this, "开启蓝牙失败", Toast.LENGTH_SHORT).show();
break;
}
}
(1)通过getBondedDevice()方法的获得已经匹配的设备。然后调用startDiscovery()方法来开始设备的搜索
public void addBluetooth(ArrayList mainList) {
Set pairedDevices = adapter.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
if (!mainList.contains(device)) {
mainList.add(device);
}
}
}
// 寻找蓝牙设备,android会将查找到的设备以广播形式发出去
adapter.startDiscovery();
}
(2)通过广播的形式来添加搜索附近蓝牙设备
private BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public 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) {//已匹配的设备
if (device != null) {
if (!mainList.contains(device)) {
mainList.add(device);
lvAdapter.notifyDataSetChanged();
}
}
}*/
if(device!=null){
if (!mainList.contains(device)) {//未包含则添加
mainList.add(device);
lvAdapter.notifyDataSetChanged();
}
}
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {//搜索结束
stopAnimation(fab);
}
Log.e("BLUE", "size = " + mainList.size());
lvAdapter.notifyDataSetChanged();
}
};
(3)动态注册并设置广播,记得销毁activity时销毁
private void initReceiver() {
IntentFilter intentFilter = new IntentFilter();// 设置广播信息过滤
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//扫描发现设备广播
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);//设备连接状态改变的广播
intentFilter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);//扫描模式变化广播
intentFilter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);//开关模式变化广播
intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束
// 注册广播接收器,接收并处理搜索结果
registerReceiver(receiver, intentFilter);
}
①蓝牙设备之间自动配对,需要两个设备都安装进行配对的apk
②由于BluetoothDevice配对的方法都是hide的,所以我们需要通过反射调用被隐藏的方法(现在基本都是通用的工具,网上模板基本一样,参考这里)
(1)配对事件
protected void connectDevice(BluetoothDevice mBluetoothDevice) {
try {
if (mBluetoothDevice.getBondState() == BluetoothDevice.BOND_NONE) {//未配对
ClsUtils.createBond(mBluetoothDevice.getClass(), mBluetoothDevice);
}else if(mBluetoothDevice.getBondState() == BluetoothDevice.BOND_BONDING){//配对中
ClsUtils.cancelBondProcess(BluetoothDevice.class, mBluetoothDevice);
} else {
ClsUtils.removeBond(BluetoothDevice.class, mBluetoothDevice);
Toast.makeText(this, "蓝牙配对解除", Toast.LENGTH_SHORT).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}
(2)蓝牙配对广播接收
public class BuletoothPairingReceiver extends BroadcastReceiver {
String strPsw = "0000";
final String ACTION_PAIRING_REQUEST = "android.bluetooth.device.action.PAIRING_REQUEST";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_PAIRING_REQUEST)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
try {
abortBroadcast();//如果没有将广播终止,则会出现一个一闪而过的配对框。
//确认配对
ClsUtils.setPairingConfirmation(device.getClass(), device, true);
//调用setPin方法进行配对...
boolean ret = ClsUtils.setPin(device.getClass(), device, strPsw);
// Toast.makeText(context, "配对信息" + device.getName()+"=="+ret, Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(context, "请求连接错误...", Toast.LENGTH_LONG).show();
}
}
}
}
}
(3)AndroidManifest静态注册广播
(4)设置蓝牙设备可见性
static public void setDiscoverableTimeout(BluetoothAdapter adapter, int timeout) {
try {
Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class);
setDiscoverableTimeout.setAccessible(true);
Method setScanMode = BluetoothAdapter.class.getMethod("setScanMode", int.class, int.class);
setScanMode.setAccessible(true);
setDiscoverableTimeout.invoke(adapter, timeout);
setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE, timeout);
} catch (Exception e) {
e.printStackTrace();
}
}
(5)关闭蓝牙设备可见性
static public void closeDiscoverableTimeout(BluetoothAdapter adapter) {
try {
Method setDiscoverableTimeout = BluetoothAdapter.class.getMethod("setDiscoverableTimeout", int.class);
setDiscoverableTimeout.setAccessible(true);
Method setScanMode = BluetoothAdapter.class.getMethod("setScanMode", int.class, int.class);
setScanMode.setAccessible(true);
setDiscoverableTimeout.invoke(adapter, 1);
setScanMode.invoke(adapter, BluetoothAdapter.SCAN_MODE_CONNECTABLE, 1);
} catch (Exception e) {
e.printStackTrace();
}
}
(1)基本流程
①主动连接:通过调用BluetoothDevice的createRfcommSocketToServiceRecord(UUID)方法,获得一个BluetoothSocket对象。通过调用connect()方法来初始化连接。在这个调用中,为了找到匹配的UUID,系统会在远程的设备上执行一个SDP查询。如果查询成功,并且远程设备接收了该连接请求,那么它会在连接期间共享使用RFCOMM通道,并且connect()方法会返回。这个方法是一个阻塞调用。如果因为某些原因,连接失败或连接超时(大约在12秒之后),就会抛出一个异常。
②被动连接:通过调用BluetoothAdapter的listenUsingRfcommWithServiceRecord(String name, UUID uuid),获得BluetoothServerSocket对象。该方法中的String参数是一个可识别的你的服务端的名称,系统会自动的把它写入设备上的Service Discovery Protocol(SDP)数据库实体(该名称是任意的,并且可以简单的使用你的应用程序的名称)。UUID参数也会被包含在SDP实体中,并且是跟客户端设备连接的基本协议。也就是说,当客户端尝试跟服务端连接时,它会携带一个它想要连接的服务端能够唯一识别的UUID。只有在这些UUID完全匹配的情况下,连接才可能被接收。通过调用accept()方法,启动连接请求。这是一个阻塞调用。只有在连接被接收或发生异常的情况下,该方法才返回。只有在发送连接请求的远程设备所携带的UUID跟监听服务套接字所注册的一个UUID匹配的时候,该连接才被接收。连接成功,accept()方法会返回一个被连接的BluetoothSocket对象。 除非你想要接收其他连接,否则在接收到一个连接套接字之后,立即调用BluetoothServerSocket对象的close()方法。该方法会释放服务套接字以及它所占用的所有资源,但不会关闭被连接的已经有accept()方法所返回的BluetoothSocket对象。
③数据传输:当你成功的连接了两个(或更多)设备时,每一个设备都有一个被连接的BluetoothSocket对象。使用BluetoothSocket对象分别通过getInputStream()和getOutputStream()方法来获得通过套接字来处理传输任务的InputStream和OutputStream对象; 用read(byte[])和write(byte[])方法来读写流中的数据。
(2)Google的蓝牙聊天源码的分析ConnectThread:主动发起蓝牙连接线程,此线程在试图与设备进行传出连接时运行,它直接通过连接成功或失败
ConnectedThread:蓝牙连接成功后启动,此线程在与远程设备连接期间运行。它处理所有传入和传出的传输
AcceptThread:监听来自其他设备的蓝牙连接,此线程在侦听传入连接时运行,它一直运行到连接被接收(或者直到被取消)
关键代码
@SuppressLint("NewApi")
public class ChatService {
private static final String TAG = "ChatService";
private static final String NAME_SECURE = "Bluetooth Secure";
private static final UUID UUID_ANDROID_DEVICE =
UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
private static final UUID UUID_OTHER_DEVICE =
UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");//蓝牙串口服务
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mSecureAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
Context context;
/**
* 准备一个新的bluetoothchat会话
*
* @param context
* @param handler
* @param mAdapter
*/
public ChatService(Context context, Handler handler, BluetoothAdapter mAdapter) {
this.mAdapter = mAdapter;
mState = ChatState.STATE_NONE;
mHandler = handler;
this.context = context;
}
/**
* 设置聊天连接的当前状态
*
* @param state
*/
private synchronized void setState(int state) {
Log.d(TAG, "setState() " + mState + " -> " + state);
mHandler.obtainMessage(ChatState.MESSAGE_STATE_CHANGE, state, mState).sendToTarget();
mState = state;
}
/**
* 返回当前连接状态
*
* @return
*/
public synchronized int getState() {
return mState;
}
/**
* 开始聊天
*/
public synchronized void start() {
/**
* 取消试图连接的任何线程
*/
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
/**
*取消当前运行连接的任何线程
*/
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(ChatState.STATE_LISTEN);
/**
* 启动线程监听一个bluetoothserversocket
*/
if (mSecureAcceptThread == null) {
mSecureAcceptThread = new AcceptThread();
mSecureAcceptThread.start();
}
}
/**
* 开始connectthread发起一个连接到一个远程设备
*
* @param device 连接的蓝牙设备
*/
public synchronized void connect(BluetoothDevice device) {
/**
* 取消试图连接的任何线程
*/
if (mState == ChatState.STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
/**
* 取消当前运行连接的任何线程
*/
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
/**
*启动线程与给定设备连接
*/
mConnectThread = new ConnectThread(device);
mConnectThread.start();
setState(ChatState.STATE_CONNECTING);
}
/**
* 开始connectedthread开始管理蓝牙连接
*
* @param socket
* @param device 已连接的蓝牙设备
*/
public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) {
Log.i(TAG, "connected");
/**
* 取消完成连接的线程
*/
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
/**
* 取消当前运行连接的任何线程
*/
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
/**
* 取消接受线程,因为我们只想连接到一个设备
*/
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
/**
* 启动线程管理连接并执行传输
*/
mConnectedThread = new ConnectedThread(socket);
mConnectedThread.start();
/**
* 将连接的设备的名称返回到UI活动.
*/
Message msg = mHandler.obtainMessage(ChatState.MESSAGE_DEVICE_INFO);
Bundle bundle = new Bundle();
bundle.putString(ChatState.DEVICE_NAME, device.getName());
bundle.putString(ChatState.DEVICE_ADDRESS, device.getAddress());
msg.setData(bundle);
mHandler.sendMessage(msg);
setState(ChatState.STATE_CONNECTED);
}
/**
* 停止所有的线程
*/
public synchronized void stop() {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread.kill();
mSecureAcceptThread = null;
}
setState(ChatState.STATE_NONE);
}
/**
* 写入
*
* @param out
*/
public void write(byte[] out) {
Log.e(TAG, "write string out=" + Bytes2HexString(out));
ConnectedThread r;
synchronized (this) {
if (mState != ChatState.STATE_CONNECTED) return;
r = mConnectedThread;
}
r.write(out);
}
/**
* byte[]转string
*/
public static String Bytes2HexString(byte[] b) {
String ret = "";
for (int i = 0; i < b.length; i++) {
String hex = Integer.toHexString(b[i] & 0xFF);
if (hex.length() == 1) {
hex = '0' + hex;
}
ret += hex.toUpperCase();
}
return ret;
}
/**
* 指示连接尝试失败,重新连接
*/
private void connectionFailed() {
ChatService.this.start();
}
/**
* 此线程在侦听传入连接时运行。它的行为像一个服务器端客户端。它一直运行到连接被接收(或者直到被取消)
*/
private class AcceptThread extends Thread {
private BluetoothServerSocket mmServerSocket;
boolean isRunning = true;
private boolean mmss() {
if (mmServerSocket == null) {
return false;
}
return true;
}
public AcceptThread() {
BluetoothServerSocket tmp = null;
/**
*创建一个新的BluetoothServerSocket监听服务器套接字
*/
try {
if (ChatState.DEVICE_ANDROID)
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_ANDROID_DEVICE);
else
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, UUID_OTHER_DEVICE);
} catch (IOException e) {
try {
throw new SocketException(context, "error-AcceptThread", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-AcceptThread");
}
}
mmServerSocket = tmp;
}
public void run() {
setName("AcceptThread");
BluetoothSocket socket = null;
Log.i(TAG, "run");
/**
* 如果没有连接,监听服务器套接字
*/
while (mState != ChatState.STATE_CONNECTED && isRunning) {
try {
/**
* 这是一个阻塞调用,只会返回成功的连接或异常
*/
if (mmss())
socket = mmServerSocket.accept();
} catch (IOException e) {
try {
throw new SocketException(context, "error-AcceptThread-run1", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-AcceptThread-run1");
}
break;
}
/**
* 如果一个连接被接受
*/
if (socket != null) {
synchronized (ChatService.this) {
switch (mState) {
case ChatState.STATE_LISTEN:
case ChatState.STATE_CONNECTING:
/**
* 正常情况。启动连接的线程
*/
connected(socket, socket.getRemoteDevice());
break;
case ChatState.STATE_NONE:
case ChatState.STATE_CONNECTED:
/**
* 要么没有准备好,要么已经连接。终止新套接字。
*/
try {
socket.close();
} catch (IOException e) {
try {
throw new SocketException(context, "error-AcceptThread-run2", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-AcceptThread-run2");
}
}
break;
}
}
}
}
}
/**
* 关闭Socket
*/
public void cancel() {
try {
if (mmss()) {
mmServerSocket.close();
}
mmServerSocket = null;
} catch (IOException e) {
try {
throw new SocketException(context, "error-cancel", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-cancel");
}
}
}
public void kill() {
isRunning = false;
}
}
/**
* 此线程在试图与设备进行传出连接时运行。它直接通过连接成功或失败
*/
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private boolean mms() {
if (mmSocket == null) {
return false;
}
return true;
}
public ConnectThread(BluetoothDevice device) {
mmDevice = device;
BluetoothSocket tmp = null;
/**
* 得到一个与给定的蓝牙设备连接BluetoothSocket
*/
try {
if (ChatState.DEVICE_ANDROID)
tmp = device.createRfcommSocketToServiceRecord(UUID_ANDROID_DEVICE);
else
tmp = device.createRfcommSocketToServiceRecord(UUID_OTHER_DEVICE);
} catch (IOException e) {
try {
throw new SocketException(context, "error-ConnectThread", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-ConnectThread");
}
}
mmSocket = tmp;
}
public void run() {
/**
* 关闭蓝牙扫描,因为它会减慢连接速度
*/
mAdapter.cancelDiscovery();
/**
* 连接BluetoothSocket
*/
try {
/**
* 这是一个阻塞调用,只会返回成功的连接或异常
*/
if (mms())
mmSocket.connect();
} catch (IOException e) {
cancel();
connectionFailed();
return;
}
/**
* 用完复位 ConnectThread
*/
synchronized (ChatService.this) {
mConnectThread = null;
}
connected(mmSocket, mmDevice);
}
/**
* 关闭Socket
*/
public void cancel() {
try {
if (mms())
mmSocket.close();
} catch (IOException e) {
try {
throw new SocketException(context, "error-cancel", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-cancel");
}
}
}
}
/**
* 此线程在与远程设备连接期间运行。它处理所有传入和传出的传输
*
*/
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private boolean mms() {
if (mmSocket == null) {
return false;
}
return true;
}
public ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
Log.i(TAG, "ConnectedThread");
/**
* 得到BluetoothSocket的输入和输出流
*/
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
try {
throw new SocketException(context, "error-ConnectedThread", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-ConnectedThread");
}
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
Log.i(TAG, "ConnectedThread mmInStream=" + mmInStream);
Log.i(TAG, "ConnectedThread mmOutStream=" + mmOutStream);
}
public void run() {
byte[] buffer;
ArrayList arr_byte = new ArrayList();
int before = 0;
/**
* 循环读取输入流
*/
while (true) {
try {
int data = mmInStream.read();
if (data == 0x0D && before == 0x0A) {
buffer = new byte[arr_byte.size()];
for (int i = 0; i < buffer.length; i++) {
buffer[i] = arr_byte.get(i).byteValue();
}
Log.i(TAG, "run arr_byte=" + arr_byte);
Log.i(TAG, "run arr_byte.size()=" + arr_byte.size());
Log.i(TAG, "run buffer=" + buffer);
Log.i(TAG, "run buffer=" + buffer.length);
mHandler.obtainMessage(ChatState.MESSAGE_READ
, buffer.length, -1, buffer).sendToTarget();
arr_byte = new ArrayList();
} else {
arr_byte.add(data);
}
before = data;
} catch (IOException e) {
connectionFailed();
break;
}
}
}
/**
* 写入输出流
*
* @param buffer
*/
public void write(byte[] buffer) {
Log.i(TAG, "write buffer=" + buffer);
Log.i(TAG, "write buffer.length=" + buffer.length);
try {
byte[] buffer2 = new byte[buffer.length + 2];
for (int i = 0; i < buffer.length; i++)
buffer2[i] = buffer[i];
buffer2[buffer2.length - 2] = 0x0A;
buffer2[buffer2.length - 1] = 0x0D;
Log.i(TAG, "write buffer2.length=" + buffer2.length);
mmOutStream.write(buffer2);
Log.i(TAG, "write mmOutStream=" + mmOutStream);
mHandler.obtainMessage(ChatState.MESSAGE_WRITE
, -1, -1, buffer).sendToTarget();
} catch (IOException e) {
try {
throw new SocketException(context, "error-write", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-write");
}
}
}
/**
* 关闭Socket
*/
public void cancel() {
try {
if (mms())
mmSocket.close();
} catch (IOException e) {
try {
throw new SocketException(context, "error-cancel", e);
} catch (SocketException e1) {
e1.printStackTrace();
Log.e("soketException", "error-cancel");
}
}
}
}
}
(2)在ChatConnectControl负责接收Handler消息进行处理,提供若干函数供UI使用。
public class ChatConnectControl {
public static String TAG = "ChatConnectControl";
/**
* 蓝牙会话监听
*/
private BluetoothChatListener mBluetoothChatListener = null;
/**
* 蓝牙默认的适配器
*/
private BluetoothAdapter mBluetoothAdapter = null;
/**
* 蓝牙聊天服务
*/
private ChatService mChatService = null;
private String mDeviceName = null;
private String mDeviceAddress = null;
/**
* 是否已连接
*/
private boolean isConnected = false;
/**
* 是否福连接中
*/
private boolean isConnecting = false;
private Context mContext;
public ChatConnectControl(Context context, BluetoothAdapter mBluetoothAdapter) {
mContext = context;
this.mBluetoothAdapter = mBluetoothAdapter;
}
/**
* 服务是否启动
*
* @return
*/
public boolean isServiceAvailable() {
return mChatService != null;
}
/**
* 初始化蓝牙聊天服务
*/
public void setupService() {
mChatService = new ChatService(mContext, mHandler, mBluetoothAdapter);
}
/**
* 蓝牙聊天当前连接状态
*
* @return
*/
public int getServiceState() {
if (mChatService != null)
return mChatService.getState();
else
return -1;
}
/**
* 启动
*/
public void startService() {
if (mChatService != null) {
if (mChatService.getState() == ChatState.STATE_NONE) {
mChatService.start();
}
}
}
/**
* 停止
*/
public void stopService() {
if (mChatService != null) {
mChatService.stop();
}
new Handler().postDelayed(new Runnable() {
public void run() {
if (mChatService != null) {
mChatService.stop();
}
}
}, 500);
}
/**
* 重启/切换
*/
public void setDeviceTarget() {
stopService();
startService();
}
/**
* 连接
*
* @param address
*/
public void connect(String address) {
Log.e(TAG, "connect address=" + address);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
mChatService.connect(device);
}
/**
* 断开
*/
public void disconnect() {
Log.i(TAG, "disconnect");
if (mChatService != null) {
mChatService.stop();
if (mChatService.getState() == ChatState.STATE_NONE) {
mChatService.start();
}
}
}
/**
* 发送
*
* @param data
*/
public void send(String data) {
if (mChatService.getState() == ChatState.STATE_CONNECTED) {
Log.i(TAG, "send data.getBytes()=" + data.getBytes());
mChatService.write(data.getBytes());
}
}
/**
* 配置监听
*
* @param listener
*/
public void setBluetoothChatListener(BluetoothChatListener listener) {
mBluetoothChatListener = listener;
}
/**
* 处理Handler接收的内容
*/
@SuppressLint("HandlerLeak")
private final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case ChatState.MESSAGE_WRITE:
byte[] readBuf_write = (byte[]) msg.obj;
Log.i(TAG, "MESSAGE_READ readBuf_write=" + readBuf_write);
Log.i(TAG, "MESSAGE_READ readBuf_write.length=" + readBuf_write.length);
Log.i(TAG, "MESSAGE_WRITE");
break;
case ChatState.MESSAGE_READ:
Log.i(TAG, "MESSAGE_READ");
byte[] readBuf = (byte[]) msg.obj;
String readMessage = new String(readBuf);
Log.i(TAG, "MESSAGE_READ readBuf=" + readBuf);
Log.i(TAG, "MESSAGE_READ readBuf.length=" + readBuf.length);
Log.i(TAG, "MESSAGE_READ readMessage=" + readMessage);
Log.i(TAG, "MESSAGE_READ readMessage.length=" + readMessage.length());
if (readBuf != null && readBuf.length > 0) {
if (mBluetoothChatListener != null) {
Log.i(TAG, "MESSAGE_READ onDataReceived");
mBluetoothChatListener.onDataReceived(readBuf, readMessage);
}
}
break;
case ChatState.MESSAGE_DEVICE_INFO:
mDeviceName = msg.getData().getString(ChatState.DEVICE_NAME);
mDeviceAddress = msg.getData().getString(ChatState.DEVICE_ADDRESS);
if (mBluetoothChatListener != null)
mBluetoothChatListener.onDeviceConnected(mDeviceName, mDeviceAddress);
Log.i(TAG, "MESSAGE_DEVICE_NAME" + mDeviceName);
Log.i(TAG, "MESSAGE_DEVICE_NAME" + mDeviceAddress);
isConnected = true;
break;
case ChatState.MESSAGE_STATE_CHANGE:
Log.i(TAG, "MESSAGE_STATE_CHANGE:" + msg.arg2 + "->" + msg.arg1);
if (mBluetoothChatListener != null)
mBluetoothChatListener.onServiceStateChanged(msg.arg2 + "->" + msg.arg1);
if (isConnected && msg.arg1 != ChatState.STATE_CONNECTED) {
if (mBluetoothChatListener != null)
mBluetoothChatListener.onDeviceDisconnected();
isConnected = false;
mDeviceName = null;
mDeviceAddress = null;
}
if (!isConnecting && msg.arg1 == ChatState.STATE_CONNECTING) {
isConnecting = true;
} else if (isConnecting) {
if (msg.arg1 != ChatState.STATE_CONNECTED) {
if (mBluetoothChatListener != null)
mBluetoothChatListener.onDeviceConnectionFailed();
}
isConnecting = false;
}
break;
}
}
};
}
(3)配置蓝牙聊天监听接口回调供UI展示
public interface BluetoothChatListener {
/**
* 设备连接中
*
* @param name
* @param address
*/
void onDeviceConnected(String name, String address);
/**
* 设备断开连接
*/
void onDeviceDisconnected();
/**
* 设备连接失败
*/
void onDeviceConnectionFailed();
/**
* 聊天状态
*
* @param state
*/
void onServiceStateChanged(String state);
/**
* 接收消息
*
* @param data
* @param message
*/
void onDataReceived(byte[] data, String message);
}
剩余UI部分这里就不贴出来了,项目已经上传至GitHub,项目包含经典蓝牙与BLE以及ibeacon,请自行阅读需要的模块,经典蓝牙模块基本实现从蓝牙配对到单聊的功能。
①关于配对在Android4.4以上(Android6.0(小米4)、Android5.1(魅蓝2)\、Andro4id.4.4.2(vivo))测试可用且不弹配对框,但是在Android4.2.2(oppo)还是会弹配对框......
②聊天传输符号有个别不识别,在Android4.2.2(oppo)接收表情不识别......
以上便关于经典蓝牙的主要内容,如果有任何不妥之处,还请各位指出。