一.概述
今天进行蓝牙实战的第三讲,讲讲蓝牙设备之间的数据传输,先看效果图
这个程序可以作为服务器端和客户端进行使用。
当作为服务器端使用时,我们点击等待连接,
作为客户端时,我们先点击已绑定设备,然后点击hello,这时候就会在已经连接上的服务器端显示hello数据。当不需要连接时,点击断开按钮,就断开了与服务器的连接。
二.工作流程
首先我们总结一下客户端和服务器端的工作流程:
服务器端:
- 通过listenUsingRfcommWithServiceRecord创建一个BlueToothServerSocket
- 监听网络accept
- 处理网络socket
关闭连接
客户端
通过createRfcommSocketToServiceRecord创建一个BlueToothSocket
- 连接服务端connet
- 处理数据
- 关闭连接
三.代码示例
public class MainActivity extends Activity {
public static final int REQUEST_CODE = 0;
private List<BluetoothDevice> mDeviceList = new ArrayList<>();
private List<BluetoothDevice> mBondedDeviceList = new ArrayList<>();
private BlueToothController mController = new BlueToothController();
private ListView mListView;
private DeviceAdapter mAdapter;
private Toast mToast;
private AcceptThread mAcceptThread;
private ConnectThread mConnectThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initActionBar();
setContentView(R.layout.activity_main);
initUI();
//注册广播
registerBluetoothReceiver();
//打开蓝牙
mController.turnOnBlueTooth(this, REQUEST_CODE);
}
private void registerBluetoothReceiver() {
IntentFilter filter = new IntentFilter();
//开始查找
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
//结束查找
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
//查找设备
filter.addAction(BluetoothDevice.ACTION_FOUND);
//设备扫描模式改变
filter.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED);
//绑定状态
filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
registerReceiver(mReceiver, filter);
}
private Handler mUIHandler = new MyHandler();
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if( BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(action) ) {
setProgressBarIndeterminateVisibility(true);
//初始化数据列表
mDeviceList.clear();
mAdapter.notifyDataSetChanged();
}
else if( BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
setProgressBarIndeterminateVisibility(false);
}
else if( BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//找到一个,添加一个
mDeviceList.add(device);
mAdapter.notifyDataSetChanged();
}
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) {
setProgressBarIndeterminateVisibility(true);
}
else {
setProgressBarIndeterminateVisibility(false);
}
}
else if( BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(action) ) {
BluetoothDevice remoteDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
if( remoteDevice == null ) {
showToast("no device");
return;
}
int status = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,0);
if( status == BluetoothDevice.BOND_BONDED) {
showToast("Bonded " + remoteDevice.getName());
}
else if( status == BluetoothDevice.BOND_BONDING){
showToast("Bonding " + remoteDevice.getName());
}
else if(status == BluetoothDevice.BOND_NONE){
showToast("Not bond " + remoteDevice.getName());
}
}
}
};
private void initUI() {
mListView = (ListView) findViewById(R.id.device_list);
mAdapter = new DeviceAdapter(mDeviceList, this);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(bindDeviceClick);
}
@Override
protected void onDestroy() {
super.onDestroy();
if( mAcceptThread != null) {
mAcceptThread.cancel();
}
if( mConnectThread != null) {
mConnectThread.cancel();
}
unregisterReceiver(mReceiver);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
private void showToast(String text) {
if( mToast == null) {
mToast = Toast.makeText(this, text, Toast.LENGTH_LONG);
}
else {
mToast.setText(text);
}
mToast.show();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.enable_visiblity) {
mController.enableVisibly(this);
}
else if( id == R.id.find_device) {
//查找设备
mAdapter.refresh(mDeviceList);
mController.findDevice();
mListView.setOnItemClickListener(bindDeviceClick);
}
else if (id == R.id.bonded_device) {
//查看已绑定设备
mBondedDeviceList = mController.getBondedDeviceList();
mAdapter.refresh(mBondedDeviceList);
mListView.setOnItemClickListener(bindedDeviceClick);
}
else if( id == R.id.listening) {
if( mAcceptThread != null) {
mAcceptThread.cancel();
}
mAcceptThread = new AcceptThread(mController.getAdapter(), mUIHandler);
mAcceptThread.start();
}
else if( id == R.id.stop_listening) {
if( mAcceptThread != null) {
mAcceptThread.cancel();
}
}
else if( id == R.id.disconnect) {
if( mConnectThread != null) {
mConnectThread.cancel();
}
}
else if( id == R.id.say_hello) {
say("Hello");
}
else if( id == R.id.say_hi) {
say("Hi");
}
return super.onOptionsItemSelected(item);
}
private void say(String word) {
if( mAcceptThread != null) {
try {
mAcceptThread.sendData(word.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
else if( mConnectThread != null) {
try {
mConnectThread.sendData(word.getBytes("utf-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
private AdapterView.OnItemClickListener bindDeviceClick = new AdapterView.OnItemClickListener() {
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
BluetoothDevice device = mDeviceList.get(i);
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
device.createBond();
}
}
};
private AdapterView.OnItemClickListener bindedDeviceClick = new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
BluetoothDevice device = mBondedDeviceList.get(i);
if( mConnectThread != null) {
mConnectThread.cancel();
}
mConnectThread = new ConnectThread(device, mController.getAdapter(), mUIHandler);
mConnectThread.start();
}
};
/**
* 设置actionbar样式,此处显示一个
*/
private void initActionBar() {
requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
getActionBar().setDisplayUseLogoEnabled(false);
setProgressBarIndeterminate(true);
try {
ViewConfiguration config = ViewConfiguration.get(this);
Field menuKeyField = ViewConfiguration.class
.getDeclaredField("sHasPermanentMenuKey");
if (menuKeyField != null) {
menuKeyField.setAccessible(true);
menuKeyField.setBoolean(config, false);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 处理消息
*/
private class MyHandler extends Handler {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case Constant.MSG_START_LISTENING:
setProgressBarIndeterminateVisibility(true);
break;
case Constant.MSG_FINISH_LISTENING:
setProgressBarIndeterminateVisibility(false);
break;
case Constant.MSG_GOT_DATA:
showToast("data: "+String.valueOf(msg.obj));
break;
case Constant.MSG_ERROR:
showToast("error: "+String.valueOf(msg.obj));
break;
case Constant.MSG_CONNECTED_TO_SERVER:
showToast("Connected to Server");
break;
case Constant.MSG_GOT_A_CLINET:
showToast("Got a Client");
break;
}
}
}
}
/**
* 客户端连接线程
*/
public class ConnectThread extends Thread {
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private BluetoothAdapter mBluetoothAdapter;
private final Handler mHandler;
private ConnectedThread mConnectedThread;
public ConnectThread(BluetoothDevice device, BluetoothAdapter adapter, Handler handler) {
BluetoothSocket tmp = null;
mmDevice = device;
mBluetoothAdapter = adapter;
mHandler = handler;
//根据给定的设备获取一个BluetoothSocket对象
try {
// 和服务器端使用相同的UUID
tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (IOException e) { }
mmSocket = tmp;
}
public void run() {
// 取消搜索设备因为会关闭连接
mBluetoothAdapter.cancelDiscovery();
try {
// Connect the device through the socket. This will block
// until it succeeds or throws an exception
mmSocket.connect();
} catch (Exception connectException) {
mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, connectException));
// Unable to connect; close the socket and get out
try {
mmSocket.close();
} catch (IOException closeException) { }
return;
}
// Do work to manage the connection (in a separate thread)
manageConnectedSocket(mmSocket);
}
private void manageConnectedSocket(BluetoothSocket mmSocket) {
mHandler.sendEmptyMessage(Constant.MSG_CONNECTED_TO_SERVER);
mConnectedThread = new ConnectedThread(mmSocket, mHandler);
mConnectedThread.start();
}
/** Will cancel an in-progress connection, and close the socket */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
public void sendData(byte[] data) {
if( mConnectedThread!=null){
mConnectedThread.write(data);
}
}
}
/**
* 服务端处理请求的线程
*/
public class AcceptThread extends Thread {
//连接的服务名称
private static final String NAME = "BlueToothClass";
//通信的UUID,注意,这个UUID是蓝牙通信的UUID,必须是这个UUID,不能使随便创建的UUID
private static final UUID MY_UUID = UUID.fromString(Constant.CONNECTTION_UUID);
private final BluetoothServerSocket mmServerSocket;
private final BluetoothAdapter mBluetoothAdapter;
private final Handler mHandler;
//连接成功后,处理请求的线程
private ConnectedThread mConnectedThread;
public AcceptThread(BluetoothAdapter adapter, Handler handler) {
// Use a temporary object that is later assigned to mmServerSocket,
// because mmServerSocket is final
mBluetoothAdapter = adapter;
mHandler = handler;
BluetoothServerSocket tmp = null;
try {
// MY_UUID is the app's UUID string, also used by the client code
//获取一个服务端Socket对象,第一个参数为服务名称,
tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
} catch (IOException e) { }
mmServerSocket = tmp;
}
public void run() {
BluetoothSocket socket = null;
// 保持监听直到异常发生或者返回了一个Socket
while (true) {
try {
//开始监听
mHandler.sendEmptyMessage(Constant.MSG_START_LISTENING);
socket = mmServerSocket.accept();
} catch (IOException e) {
//发送异常的信息
mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));
break;
}
// 如果接受到了一个连接,就会获得一个socket对象
if (socket != null) {
// 处理连接,在单独的线程中
manageConnectedSocket(socket);
try {
mmServerSocket.close();
mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
private void manageConnectedSocket(BluetoothSocket socket) {
//为了简化,只支持同时处理一个连接,实际项目中可能会有多个连接,就需要连接池了
if( mConnectedThread != null) {
mConnectedThread.cancel();
}
mHandler.sendEmptyMessage(Constant.MSG_GOT_A_CLINET);
mConnectedThread = new ConnectedThread(socket, mHandler);
mConnectedThread.start();
}
/** Will cancel the listening socket, and cause the thread to finish */
public void cancel() {
try {
mmServerSocket.close();
mHandler.sendEmptyMessage(Constant.MSG_FINISH_LISTENING);
} catch (IOException e) { }
}
public void sendData(byte[] data) {
if( mConnectedThread!=null){
mConnectedThread.write(data);
}
}
}
public class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
private final Handler mHandler;
public ConnectedThread(BluetoothSocket socket, Handler handler) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
mHandler = handler;
// 获取输入输出流
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) { }
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024]; // buffer store for the stream
int bytes;
// Keep listening to the InputStream until an exception occurs
while (true) {
try {
// 从输入流读取数据
bytes = mmInStream.read(buffer);
// Send the obtained bytes to the UI activity
if( bytes >0) {
Message message = mHandler.obtainMessage(Constant.MSG_GOT_DATA, new String(buffer, 0, bytes, "utf-8"));
mHandler.sendMessage(message);
}
Log.d("GOTMSG", "message size" + bytes);
} catch (IOException e) {
mHandler.sendMessage(mHandler.obtainMessage(Constant.MSG_ERROR, e));
break;
}
}
}
/* 发送数据岛远程设备*/
public void write(byte[] bytes) {
try {
mmOutStream.write(bytes);
} catch (IOException e) { }
}
/* 关闭连接 */
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) { }
}
}
最后给出源码的下载位置