一、蓝牙通信原理介绍
Android 平台包含蓝牙网络堆栈支持,此支持能让设备以无线方式与其他蓝牙设备交换数据。应用框架提供通过 Android Bluetooth API 访问蓝牙功能的权限。这些 API 允许应用以无线方式连接到其他蓝牙设备,从而实现点到点和多点无线功能。
蓝牙通信和socket通信原理基本上是一致的,下图为Socket通信图:
左为客户端Socket连接的一个流程,右为服务端Socket操作流程。
客户端Socket:
1、创建客户端蓝牙Sokcet
2、创建连接
3、读写数据
4、关闭
服务端Socket:
1、创建服务端蓝牙Socket
2、绑定端口号(蓝牙忽略)
3、创建监听listen(蓝牙忽略, 蓝牙没有此监听,而是通过whlie(true)死循环来一直监听的)
4、通过accept(),如果有客户端连接,会创建一个新的Socket,体现出并发性,可以同时与多个socket通讯)
5、读写数据
6、关闭
二、安卓实现蓝牙通信
1.重要的类和接口
BluetoothAdapter:代表本地蓝牙适配器(蓝牙无线电)。BluetoothAdapter是所有蓝牙交互的入口。使用这个你可以发现其他蓝牙设备,查询已配对的设备列表,使用一个已知的MAC地址来实例化一个BluetoothDevice,以及创建一个BluetoothServerSocket来为监听与其他设备的通信。
BluetoothDevice:代表一个远程蓝牙设备,使用这个来请求一个与远程设备的BluetoothSocket连接,或者查询关于设备名称、地址、类和连接状态等设备信息。
BluetoothSocket:代表一个蓝牙socket的接口(和TCP Socket类似)。这是一个连接点,它允许一个应用与其他蓝牙设备通过InputStream和OutputStream交换数据。
BluetoothServerSocket:代表一个开放的服务器socket,它监听接受的请求(与TCP ServerSocket类似)。为了连接两台Android设备,一个设备必须使用这个类开启一个服务器socket。当一个远程蓝牙设备开始一个和该设备的连接请求,BluetoothServerSocket将会返回一个已连接的BluetoothSocket,接受该连接。
2.蓝牙权限
首先需要AndroidManifest.xml文件中添加操作蓝牙的权限。
<uses-permissionandroid:name="android.permission.BLUETOOTH" />
允许程序连接到已配对的蓝牙设备。
<uses-permissionandroid:name="android.permission.BLUETOOTH_ADMIN" />
允许程序发现和配对蓝牙设备。
3.客户端
public class ConnectThread extends Thread{
private static final UUID MY_UUID= UUID.fromString(Constant.CONNECTTION_UUID);
/** 客户端socket*/
private final BluetoothSocket mmSoket;
/** 要连接的设备*/
private final BluetoothDevice mmDevice;
private BluetoothAdapter mBluetoothAdapter;
/** 主线程通信的Handler*/
private final Handler mHandler;
/** 发送和接收数据的处理类*/
private ConnectedThread mConnectedThread;
public ConnectThread(BluetoothDevice device, BluetoothAdapter bluetoothAdapter, Handler mUIhandler) {
mmDevice = device;
mBluetoothAdapter = bluetoothAdapter;
mHandler = mUIhandler;
BluetoothSocket tmp = null;
try {
// 创建客户端Socket
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) {
e.printStackTrace();
}
mmSoket = tmp;
}
@Override
public void run() {
super.run();
// 关闭正在发现设备.(如果此时又在查找设备,又在发送数据,会有冲突,影响传输效率)
mBluetoothAdapter.cancelDiscovery();
try {
// 连接服务器
mmSoket.connect();
} catch (IOException e) {
// 连接异常就关闭
try {
mmSoket.close();
} catch (IOException e1) {
}
return;
}
manageConnectedSocket(mmSoket);
}
private void manageConnectedSocket(BluetoothSocket mmSoket) {
// 通知主线程连接上了服务端socket,更新UI
mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);
// 新建一个线程进行通讯,不然会发现线程堵塞
mConnectedThread = new ConnectedThread(mmSoket,mHandler);
mConnectedThread.start();
}
/**
* 关闭当前客户端
*/
public void cancle() {
try {
mmSoket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
* @param data
*/
public void sendData(byte[] data) {
if(mConnectedThread != null) {
mConnectedThread.write(data);
}
}
}
4.服务端
public class AcceptThread extends Thread {
/** 连接的名称*/
private static final String NAME = "BluetoothClass";
/** UUID*/
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
/** 服务端蓝牙Sokcet*/
private final BluetoothServerSocket mmServerSocket;
private final BluetoothAdapter mBluetoothAdapter;
/** 线程中通信的更新UI的Handler*/
private final Handler mHandler;
/** 监听到有客户端连接,新建一个线程单独处理,不然在此线程中会堵塞*/
private ConnectedThread mConnectedThread;
public AcceptThread(BluetoothAdapter adapter, Handler handler) throws IOException {
mBluetoothAdapter = adapter;
this.mHandler = handler;
// 获取服务端蓝牙socket
mmServerSocket = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
}
@Override
public void run() {
super.run();
// 连接的客户端soacket
BluetoothSocket socket = null;
// 服务端是不退出的,要一直监听连接进来的客户端,所以是死循环
while (true){
// 通知主线程更新UI,客户端开始监听
mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);
try {
// 获取连接的客户端socket
socket = mmServerSocket.accept();
} catch (IOException e) {
// 通知主线程更新UI, 获取异常
mHandler.sendEmptyMessage(Constant.MSG_ERROR);
e.printStackTrace();
// 服务端退出一直监听线程
break;
}
if(socket != null) {
// 管理连接的客户端socket
manageConnectSocket(socket);
// 这里应该是手动断开,案例应该是只保证连接一个客户端,所以连接完以后,关闭了服务端socket
// try {
// mmServerSocket.close();
// mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
// } catch (IOException e) {
// e.printStackTrace();
// }
}
}
}
/**
* 管理连接的客户端socket
* @param socket
*/
private void manageConnectSocket(BluetoothSocket socket) {
// 只支持同时处理一个连接
// mConnectedThread不为空,踢掉之前的客户端
if(mConnectedThread != null) {
mConnectedThread.cancle();
}
// 主线程更新UI,连接到了一个客户端
mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);
// 新建一个线程,处理客户端发来的数据
mConnectedThread = new ConnectedThread(socket, mHandler);
mConnectedThread.start();
}
/**
* 断开服务端,结束监听
*/
public void cancle() {
try {
mmServerSocket.close();
mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 发送数据
* @param data
*/
public void sendData(byte[] data){
if(mConnectedThread != null) {
mConnectedThread.write(data);
}
}
}
5.共同通讯处理类
public class ConnectedThread extends Thread{
/** 当前连接的客户端BluetoothSocket*/
private final BluetoothSocket mmSokcet;
/** 读取数据流*/
private final InputStream mmInputStream;
/** 发送数据流*/
private final OutputStream mmOutputStream;
/** 与主线程通信Handler*/
private Handler mHandler;
private String TAG = "ConnectedThread";
public ConnectedThread(BluetoothSocket socket,Handler handler) {
mmSokcet = socket;
mHandler = handler;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
mmInputStream = tmpIn;
mmOutputStream = tmpOut;
}
@Override
public void run() {
super.run();
byte[] buffer = new byte[1024];
while (true) {
try {
// 读取数据
int bytes = mmInputStream.read(buffer);
if(bytes > 0) {
String data = new String(buffer,0,bytes,"utf-8");
// 把数据发送到主线程, 此处还可以用广播
Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA,data);
mHandler.sendMessage(message);
}
Log.d(TAG, "messge size :" + bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 踢掉当前客户端
public void cancle() {
try {
mmSokcet.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 服务端发送数据
* @param data
*/
public void write(byte[] data) {
try {
mmOutputStream.write(data);
} catch (IOException e) {
e.printStackTrace();
}
}
}
6.BlueToothController蓝牙控制类
public class BlueToothController {
private BluetoothAdapter mAapter;
public BlueToothController() {
mAapter = BluetoothAdapter.getDefaultAdapter();
}
public BluetoothAdapter getAdapter() {
return mAapter;
}
/**
* 打开蓝牙
* @param activity
* @param requestCode
*/
public void turnOnBlueTooth(Activity activity, int requestCode) {
Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
activity.startActivityForResult(intent, requestCode);
// mAdapter.enable();
}
/**
* 打开蓝牙可见性
* @param context
*/
public void enableVisibly(Context context) {
Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
context.startActivity(discoverableIntent);
}
/**
* 查找设备
*/
public void findDevice() {
assert (mAapter != null);
mAapter.startDiscovery();
}
/**
* 获取绑定设备
* @return
*/
public List<BluetoothDevice> getBondedDeviceList() {
return new ArrayList<>(mAapter.getBondedDevices());
}
}
蓝牙设备adaptor代码DeviceAdapter
public abstract class DeviceAdapter extends BaseAdapter {
private Context mContext;
private List<BluetoothDevice> mDate;
public DeviceAdapter(List<BluetoothDevice> Date, Context context){
mDate = Date;
mContext = context;
}
@Override
public int getCount() {
return mDate.size();
}
@Override
public Object getItem(int position) {
return mDate.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if(convertView==null){
viewHolder = new ViewHolder();
viewHolder.textView1 = (TextView)convertView.findViewById(R.id.textView1);
viewHolder.textView2 = (TextView)convertView.findViewById(R.id.textView2);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
//获取蓝牙设备
BluetoothDevice bluetoothDevice = (BluetoothDevice) getItem(position);
viewHolder.textView1.setText("Name="+bluetoothDevice.getName());
viewHolder.textView2.setText("Address"+bluetoothDevice.getAddress());
return convertView;
}
public class ViewHolder{
public TextView textView1;
public TextView textView2;
}
public void refresh(List<BluetoothDevice> data){
mDate = data;
notifyDataSetChanged();
}
}
7.蓝牙device接受广播Receiver
private BroadcastReceiver mReceiverBluetoothStatus = new BroadcastReceiver() {
@Override
public void onReceive(Context context,Intent intent) {
String action = intent.getAction();
if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action)) {
// 开始搜索 ——接收广播
Log.d(TAG,"开始搜索");
mList.clear();
mAdapter.refresh(mList);
} else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
// 查找到设备完成 —— 接收广播
Log.d(TAG,"查找到设备完成");
} else if (BluetoothDevice.ACTION_FOUND.equals(action)) {
// 搜索到设备 —— 接收广播
Log.d(TAG,"搜索到设备");
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
mList.add(device);
mAdapter.refresh(mList);
} else if (BluetoothAdapter.ACTION_SCAN_MODE_CHANGED.equals(action)) {
// 当自己设备设置蓝牙可见时或者不可见时 —— 接收广播
int scanMode = intent.getIntExtra(BluetoothAdapter.EXTRA_SCAN_MODE,0);
// 可见时
if (scanMode == BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Log.d(TAG,"设备可见监听");
} else {
Log.d(TAG,"设备不可见监听");
}
} else if (BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action)) {
// 绑定状态改变回调
BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if (remoteDevice == null) {
Log.d(TAG,"没有绑定设备");
return;
}
int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
if (status == BluetoothDevice.BOND_BONDED) {
Log.d(TAG,"绑定设备完成: " + remoteDevice.getName());
} else if (status == BluetoothDevice.BOND_BONDING) {
Log.d(TAG,"绑定设备中: " + remoteDevice.getName());
} else if (status == BluetoothDevice.BOND_NONE) {
Log.d(TAG,"取消绑定: ");
}
}
}
};