上一篇中讲到了蓝牙的开启和搜索,这一篇中将讲解蓝牙的连接配对及简单的通信。
本篇主要运用了多线程,I/O输入输出流,Handler等。
Android的蓝牙通信与Socket套接字相似,蓝牙端的监听接口和TCP的端口类似,使用了Socket和ServerSocket类。
蓝牙的服务器端为BluetoothServerSocket。
蓝牙的客户端为BluetoothSocket。
蓝牙的端口是RFCOMM,RFCOMM是一个面向连接,通过蓝牙模块进行的数据流传输方式,也称为串行端口规范(Serial Port Profile , SPP),为创建一个BluetoothSocket去连接到一个已知的设备,使用BluetoothDevice.createRfcommSocketToServiceRecord(),然后调用connect()方法去尝试连接。
示例效果
发送消息手机
接收消手机
在蓝牙连接通信中,将主要利用三个线程。
服务器端监听线程:
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
private class AcceptThread extends Thread {
private final BluetoothServerSocket mServerSocket; private boolean isCancel = false; public AcceptThread() { Log.d(TAG, "AcceptThread"); BluetoothServerSocket tmp = null; try { //监听RFCOMM tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord( "Bluetooth_Chat_Room", UUID.fromString(UUID_STR)); } catch (IOException e) { } mServerSocket = tmp; } public void run() { BluetoothSocket socket = null; while (true) { try { // 阻塞等待 socket = mServerSocket.accept(); } catch (IOException e) { if (!isCancel) { try { mServerSocket.close(); } catch (IOException e1) { } // 异常结束时,再次监听 mAcceptThread = new AcceptThread(); mAcceptThread.start(); } break; } if (socket != null) { // 管理已经连接的客户端 manageConnectedSocket(socket); try { mServerSocket.close(); } catch (IOException e) { } mAcceptThread = null; break; } } } public void cancel() { try { Log.d(TAG, "AcceptThread canceled"); isCancel = true; mServerSocket.close(); mAcceptThread = null; if (mCommThread != null && mCommThread.isAlive()) { mCommThread.cancel(); } } catch (IOException e) { } } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
private class ConnectThread extends Thread {
private final BluetoothSocket mSocket; private final BluetoothDevice mDevice; public ConnectThread(BluetoothDevice device) { Log.d(TAG, "ConnectThread"); BluetoothSocket tmp = null; mDevice = device; try { //创建RFCOMM通道 tmp = device.createRfcommSocketToServiceRecord(UUID .fromString(UUID_STR)); } catch (IOException e) { Log.d(TAG, "createRfcommSocketToServiceRecord error!"); } mSocket = tmp; } public BluetoothDevice getDevice() { return mDevice; } public void run() { // 取消设备扫描 mBluetoothAdapter.cancelDiscovery(); try { // 连接远程服务器设备 mSocket.connect(); } catch (IOException connectException) { Log.e(TAG, "Connect server failed"); try { mSocket.close(); } catch (IOException closeException) { } // 连接服务器失败,则自己作为服务器监听 mAcceptThread = new AcceptThread(); mAcceptThread.start(); return; } manageConnectedSocket(mSocket); } public void cancel() { try { mSocket.close(); } catch (IOException e) { } mConnectThread = null; } } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
private class ConnectedThread extends Thread {
private final BluetoothSocket mSocket; private final InputStream mInStream; private final OutputStream mOutStream; private BufferedWriter mBw; public ConnectedThread(BluetoothSocket socket) { Log.d(TAG, "ConnectedThread"); mSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { } mInStream = tmpIn; mOutStream = tmpOut; // 获得远程设备的输出缓存字符流 mBw = new BufferedWriter(new PrintWriter(mOutStream)); } public OutputStream getOutputStream() { return mOutStream; } public boolean write(String msg) { if (msg == null) return false; try { mBw.write(msg + "\n"); mBw.flush(); System.out.println("Write:" + msg); } catch (IOException e) { return false; } return true; } public String getRemoteName() { return mSocket.getRemoteDevice().getName(); } public void cancel() { try { mSocket.close(); } catch (IOException e) { } mCommThread = null; } public void run() { android.os.Message handlerMsg; String buffer = null; // 获得远程设备的缓存字符输入流 BufferedReader br = new BufferedReader(new InputStreamReader( mInStream)); while (true) { try { // 读取远程设备的一行数据 buffer = br.readLine(); System.out.println("收到:" + buffer); if (buffer == null) continue; // 通过Handler更新到UI上 handlerMsg = mHandler.obtainMessage(); handlerMsg.what = TASK_RECV_MSG; handlerMsg.obj = buffer; mHandler.sendMessage(handlerMsg); } catch (IOException e) { try { mSocket.close(); } catch (IOException e1) { } mCommThread = null; break; } } } } |
1
2 3 4 5 |
private void manageConnectedSocket(BluetoothSocket socket) {
// 启动连接管理线程 mCommThread = new ConnectedThread(socket); mCommThread.start(); } |
布局文件
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:id="@+id/XianShi" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="无内容" /> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <EditText android:id="@+id/SendText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0" android:hint="请输入" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:onClick="MySend" android:text="发送" /> </LinearLayout> <Button android:id="@+id/FindBt" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="搜索" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:text="蓝牙列表" /> <ListView android:id="@+id/DeviceList" android:layout_width="match_parent" android:layout_height="wrap_content" > </ListView> </LinearLayout> |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
public class MainActivity extends Activity {
//调试时使用 private static final String TAG = "Main"; // 使能请求码 private static final int REQUES_BT_ENABLE_CODE = 0x1002; //发送与接收到消息 public static final int TASK_SEND_MSG = 1; public static final int TASK_RECV_MSG = 2; // UUID号,表示不同的数据协议 private final String UUID_STR = "00001101-0000-1000-8000-00805F9B34FB"; private EditText mET; //发送消息输入框 private Button MyScan; //搜索蓝牙按钮 private TextView MyMessage; //显示接收到的消息文本框 private ListView MyBtList; //显示搜索到的蓝牙列表 private BluetoothAdapter mBluetoothAdapter; //蓝牙适配器 private BluetoothDevice Selectdevice; //保存连接的蓝牙地址 private AcceptThread mAcceptThread; //作为服务端监听线程 private ConnectThread mConnectThread; //作为客服端请求线程 private ConnectedThread mCommThread; //连接管理线程,负责通信 private ArrayAdapter<String> adapter; //Listview的适配器 private ArrayList<String> mArrayAdapter = new ArrayList<String>(); //ListView的显示内容 private ArrayList<BluetoothDevice> mDeviceList = new ArrayList<BluetoothDevice>(); //用于保存搜索到的蓝牙,避免重复添加 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); //请求滚动条 setContentView(R.layout.activity_main); //注册UI mET = (EditText) findViewById(R.id.SendText); MyScan = (Button) findViewById(R.id.FindBt); MyMessage = (TextView) findViewById(R.id.XianShi); MyBtList = (ListView) findViewById(R.id.DeviceList); //适配ListView adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mArrayAdapter); MyBtList.setAdapter(adapter); openBtDevice(); MyScan.setOnClickListener(new MyScanClick()); //搜索按钮监听 MyBtList.setOnItemClickListener(new MyBtConnect()); //ListView中蓝牙选择连接监听 IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND); filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); // 动态注册广播接收器 // 用来接收扫描到的设备信息 registerReceiver(mReceiver, filter); } //ListView中蓝牙选择连接监听事件 private class MyBtConnect implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String s = adapter.getItem(position); //获取位置 String address = s.substring(s.indexOf("\n") + 1).trim(); //获取蓝牙地址 Selectdevice = mBluetoothAdapter.getRemoteDevice(address); //获得远程设备 mConnectThread = new ConnectThread(Selectdevice); //建立客户端线程 mConnectThread.start(); } } //搜索蓝牙监听事件 private class MyScanClick implements OnClickListener { @Override public void onClick(View v) { // 蓝牙没有正在搜索 if (!mBluetoothAdapter.isDiscovering()) { //服务器端线程未建立或蓝牙未开启 if (mAcceptThread == null) { openBtDevice(); //开启蓝牙或建立服务器端线程 return; } findBtDevice(); //显示已配对蓝牙 mBluetoothAdapter.startDiscovery(); //开始搜索 setProgressBarIndeterminateVisibility(true); //显示滚动条 } } } //广播实时更新搜索到蓝牙 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub String action = intent.getAction(); if (BluetoothDevice.ACTION_FOUND.equals(action)) { BluetoothDevice device = intent .getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); if (mDeviceList.contains(device)) { return; } mArrayAdapter .add(device.getName() + "\n" + device.getAddress()); mDeviceList.add(device); adapter.notifyDataSetChanged(); } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED .equals(action)) { setProgressBarIndeterminateVisibility(false); //搜索结束,关闭滚动条 } } }; //显示已配对蓝牙 private void findBtDevice() { Set<BluetoothDevice> pairedDevices = mBluetoothAdapter .getBondedDevices(); if (pairedDevices.size() > 0) { for (BluetoothDevice device : pairedDevices) { if (mDeviceList.contains(device)) { return; } mArrayAdapter .add(device.getName() + "\n" + device.getAddress()); mDeviceList.add(device); } } adapter.notifyDataSetChanged(); } //打开蓝牙,启动启动服务器端线程 private boolean openBtDevice() { // 获得蓝牙匹配器 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); // 蓝牙设备不被支持 if (mBluetoothAdapter == null) { Log.e(TAG, "Your device is not support Bluetooth!"); Toast.makeText(this, "该设备没有蓝牙设备", Toast.LENGTH_LONG).show(); return false; } // 使能蓝牙设备 if (!mBluetoothAdapter.isEnabled()) { // 隐式Intent Intent enableBtIntent = new Intent( BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(enableBtIntent, REQUES_BT_ENABLE_CODE); } else { // 如果蓝牙设备已经使能,直接启动服务器端线程 mAcceptThread = new AcceptThread(); mAcceptThread.start(); } return true; } // 当startActivityForResult启动的 画面结束的时候,该方法被回调 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { // if (requestCode == REQUES_BT_ENABLE_CODE && resultCode == RESULT_OK) { // 直接启动服务器端线程 mAcceptThread = new AcceptThread(); mAcceptThread.start(); } } //服务端监听线程 private class AcceptThread extends Thread { private final BluetoothServerSocket mServerSocket; private boolean isCancel = false; public AcceptThread() { Log.d(TAG, "AcceptThread"); BluetoothServerSocket tmp = null; try { //监听RFCOMM tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord( "Bluetooth_Chat_Room", UUID.fromString(UUID_STR)); } catch (IOException e) { } mServerSocket = tmp; } public void run() { BluetoothSocket socket = null; while (true) { try { // 阻塞等待 socket = mServerSocket.accept(); } catch (IOException e) { if (!isCancel) { try { mServerSocket.close(); } catch (IOException e1) { } // 异常结束时,再次监听 mAcceptThread = new AcceptThread(); mAcceptThread.start(); } break; } if (socket != null) { // 管理已经连接的客户端 manageConnectedSocket(socket); try { mServerSocket.close(); } catch (IOException e) { } mAcceptThread = null; break; } } } public void cancel() { try { Log.d(TAG, "AcceptThread canceled"); isCancel = true; mServerSocket.close(); mAcceptThread = null; if (mCommThread != null && mCommThread.isAlive()) { mCommThread.cancel(); } } catch (IOException e) { } } } //客户端线程 private class ConnectThread extends Thread { private final BluetoothSocket mSocket; private final BluetoothDevice mDevice; public ConnectThread(BluetoothDevice device) { Log.d(TAG, "ConnectThread"); BluetoothSocket tmp = null; mDevice = device; try { //创建RFCOMM通道 tmp = device.createRfcommSocketToServiceRecord(UUID .fromString(UUID_STR)); } catch (IOException e) { Log.d(TAG, "createRfcommSocketToServiceRecord error!"); } mSocket = tmp; } public BluetoothDevice getDevice() { return mDevice; } public void run() { // 取消设备扫描 mBluetoothAdapter.cancelDiscovery(); try { // 连接远程服务器设备 mSocket.connect(); } catch (IOException connectException) { Log.e(TAG, "Connect server failed"); try { mSocket.close(); } catch (IOException closeException) { } // 连接服务器失败,则自己作为服务器监听 mAcceptThread = new AcceptThread(); mAcceptThread.start(); return; } manageConnectedSocket(mSocket); } public void cancel() { try { mSocket.close(); } catch (IOException e) { } mConnectThread = null; } } //连接管理线程,负责通信 private class ConnectedThread extends Thread { private final BluetoothSocket mSocket; private final InputStream mInStream; private final OutputStream mOutStream; private BufferedWriter mBw; public ConnectedThread(BluetoothSocket socket) { Log.d(TAG, "ConnectedThread"); mSocket = socket; InputStream tmpIn = null; OutputStream tmpOut = null; try { tmpIn = socket.getInputStream(); tmpOut = socket.getOutputStream(); } catch (IOException e) { } mInStream = tmpIn; mOutStream = tmpOut; // 获得远程设备的输出缓存字符流 mBw = new BufferedWriter(new PrintWriter(mOutStream)); } public OutputStream getOutputStream() { return mOutStream; } public boolean write(String msg) { if (msg == null) return false; try { mBw.write(msg + "\n"); mBw.flush(); System.out.println("Write:" + msg); } catch (IOException e) { return false; } return true; } public String getRemoteName() { return mSocket.getRemoteDevice().getName(); } public void cancel() { try { mSocket.close(); } catch (IOException e) { } mCommThread = null; } public void run() { android.os.Message handlerMsg; String buffer = null; // 获得远程设备的缓存字符输入流 BufferedReader br = new BufferedReader(new InputStreamReader( mInStream)); while (true) { try { // 读取远程设备的一行数据 buffer = br.readLine(); System.out.println("收到:" + buffer); if (buffer == null) continue; // 通过Handler更新到UI上 handlerMsg = mHandler.obtainMessage(); handlerMsg.what = TASK_RECV_MSG; handlerMsg.obj = buffer; mHandler.sendMessage(handlerMsg); } catch (IOException e) { try { mSocket.close(); } catch (IOException e1) { } mCommThread = null; break; } } } } private void manageConnectedSocket(BluetoothSocket socket) { // 启动连接管理线程 mCommThread = new ConnectedThread(socket); mCommThread.start(); } //发送消息按钮事件 public void MySend(View v) { String msg = mET.getText().toString().trim(); if (msg.length() <= 0) { Toast.makeText(this, "消息不能为空", Toast.LENGTH_SHORT).show(); return; } mCommThread.write(msg); android.os.Message SendMsg = new Message(); SendMsg = mHandler.obtainMessage(); SendMsg.what = TASK_SEND_MSG; SendMsg.obj = msg; mHandler.sendMessage(SendMsg); mET.setText(""); } //Handler,负责将通信的信息更新到UI上显示 private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case TASK_SEND_MSG: Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show(); break; case TASK_RECV_MSG: // 获得远程设备发送的消息 MyMessage.setText(msg.obj.toString()); break; } } }; } |