2013.09.05——— android 蓝牙聊天室之官方例子
http://lipeng88213.iteye.com/blog/1937036
蓝牙开发的大致流程:
1、蓝牙权限
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
2、认设备是否支持蓝牙
- /**
- * 确认设备是否支持蓝牙
- * 如果getDefaultAdapter()返回null,则这个设备不支持蓝牙
- */
- mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
- // If the adapter is null, then Bluetooth is not supported
- //手机不支持蓝牙
- if (mBluetoothAdapter == null) {
- Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
- finish();
- return;
- }
3、确定蓝牙能够使用
- /**
- * 确定蓝牙能够使用。
- * 通过isEnabled()来检查蓝牙当前是否可用。
- * 如果这个方法返回false,则蓝牙不能够使用。这个时候 就请求蓝牙使用,通过intent来请求
- * 在onActivityResult()里面判断
- */
- if (!mBluetoothAdapter.isEnabled()) {
- Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
- startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
- // Otherwise, setup the chat session
- }
会打开一个对话框中显示请求使用蓝牙权限。如果响应"Yes",这个进程完成(或失败)后你的应用将能够使用蓝牙,选择no,就结束应用
- case REQUEST_ENABLE_BT:
- // When the request to enable Bluetooth returns
- if (resultCode == Activity.RESULT_OK) {
- // Bluetooth is now enabled, so set up a chat session
- //请求蓝牙成功,这个时候 蓝也可用
- setupChat();
- } else {
- // User did not enable Bluetooth or an error occurred
- //请求蓝牙失败,退出应用
- Log.d(TAG, "BT not enabled");
- Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
- finish();
- }
4、最为服务器端,你必须让其他设备能够看到你,才能跟你建立连接
- //设置自己可以被其他设备搜索到
- private void ensureDiscoverable() {
- if(D) Log.d(TAG, "ensure discoverable");
- if (mBluetoothAdapter.getScanMode() !=
- BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
- Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
- discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
- startActivity(discoverableIntent);
- }
- }
5、找已经匹配的设备
在搜索设备前,查询配对设备看需要的设备是否已经是已经存在是很值得的,可以调用getBondedDevices()来做到,该函数会返回一个描述配对设备BluetoothDevice的结果集
- //查找已经匹配的设备
- Set<BluetoothDevice> pairedDevices = mBtAdapter.getBondedDevices();
- // If there are paired devices, add each one to the ArrayAdapter
- if (pairedDevices.size() > 0) {
- findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
- for (BluetoothDevice device : pairedDevices) {
- mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }
- } else {
- String noDevices = getResources().getText(R.string.none_paired).toString();
- mPairedDevicesArrayAdapter.add(noDevices);
- }
6、扫描设备
- /**
- * Start device discover with the BluetoothAdapter
- */
- private void doDiscovery() {
- if (D) Log.d(TAG, "doDiscovery()");
- // Indicate scanning in the title
- setProgressBarIndeterminateVisibility(true);
- setTitle(R.string.scanning);
- // Turn on sub-title for new devices
- findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
- // If we're already discovering, stop it
- //如果正在搜索 就先停止
- if (mBtAdapter.isDiscovering()) {
- mBtAdapter.cancelDiscovery();
- }
- // Request discover from BluetoothAdapter
- //搜索设备 用BroadcastReceiver接受BluetoothDevice.ACTION_FOUND
- mBtAdapter.startDiscovery();
- }
要开始搜索设备,只需简单的调用startDiscovery() 。该函数时异步的,调用后立即返回,返回值表示搜索是否成功开始。可以再BroadcastReceiver里面监听结果
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- // When discovery finds a device
- if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- // Get the BluetoothDevice object from the Intent
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // If it's already paired, skip it, because it's been listed already
- if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
- mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }
- // When discovery is finished, change the Activity title
- }
- };
7、作为客户端连接
扫描出来的列表,单击任意一个item,得到这个设备的mac地址和name
- // The on-click listener for all devices in the ListViews
- private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
- public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
- // Cancel discovery because it's costly and we're about to connect
- //停止搜索
- mBtAdapter.cancelDiscovery();
- // Get the device MAC address, which is the last 17 chars in the View
- String info = ((TextView) v).getText().toString();
- String address = info.substring(info.length() - 17);
- // Create the result Intent and include the MAC address
- Intent intent = new Intent();
- intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
- // Set result and finish this Activity
- setResult(Activity.RESULT_OK, intent);
- finish();
- }
- };
根据mac地址 得到远端BluetoothDevice
- BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
然后 根据device得到BluetoothSocket,并建立连接
- private class ConnectThread extends Thread {
- private final BluetoothSocket mmSocket;
- private final BluetoothDevice mmDevice;
- private String mSocketType;
- public ConnectThread(BluetoothDevice device, boolean secure) {
- mmDevice = device;
- BluetoothSocket tmp = null;
- mSocketType = secure ? "Secure" : "Insecure";
- // Get a BluetoothSocket for a connection with the
- // given BluetoothDevice
- //来生成一个BluetoothSocket对象
- //这个uuid必须服务器和客户端是同一个
- try {
- if (secure) {
- tmp = device.createRfcommSocketToServiceRecord(
- MY_UUID_SECURE);
- } else {
- tmp = device.createInsecureRfcommSocketToServiceRecord(
- MY_UUID_INSECURE);
- }
- } catch (IOException e) {
- Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
- }
- mmSocket = tmp;
- }
- public void run() {
- Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
- //设置当前线程名字
- setName("ConnectThread" + mSocketType);
- // Always cancel discovery because it will slow down a connection
- //因为扫描设备 很浪费资源 停止扫描
- mAdapter.cancelDiscovery();
- // Make a connection to the BluetoothSocket
- //用connect()完成连接
- //系统会在远程设备上完成一个SDP查找来匹配UUID。
- //如果查找成功并且远程设备接受连接,就共享RFCOMM信道,connect()会返回。
- //这也是一个阻塞的调用,不管连接失败还是超时(12秒)都会抛出异常。
- try {
- // This is a blocking call and will only return on a
- // successful connection or an exception
- mmSocket.connect();
- } catch (IOException e) {
- // Close the socket
- try {
- mmSocket.close();
- } catch (IOException e2) {
- Log.e(TAG, "unable to close() " + mSocketType +
- " socket during connection failure", e2);
- }
- connectionFailed();
- return;
- }
- // Reset the ConnectThread because we're done
- synchronized (BluetoothChatService.this) {
- mConnectThread = null;
- }
- // Start the connected thread
- //启动连接成功 可以通信的线程
- connected(mmSocket, mmDevice, mSocketType);
- }
- public void cancel() {
- try {
- mmSocket.close();
- } catch (IOException e) {
- Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
- }
- }
- }
8、作为服务器端建立连接
- private class AcceptThread extends Thread {
- // The local server socket
- private final BluetoothServerSocket mmServerSocket;
- private String mSocketType;
- public AcceptThread(boolean secure) {
- BluetoothServerSocket tmp = null;
- mSocketType = secure ? "Secure":"Insecure";
- // Create a new listening server socket
- //通过调用listenUsingRfcommWithServiceRecord(String, UUID)得到一个BluetoothServerSocket对象
- try {
- if (secure) {
- tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
- MY_UUID_SECURE);
- } else {
- tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
- NAME_INSECURE, MY_UUID_INSECURE);
- }
- } catch (IOException e) {
- Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
- }
- mmServerSocket = tmp;
- }
- public void run() {
- if (D) Log.d(TAG, "Socket Type: " + mSocketType +
- "BEGIN mAcceptThread" + this);
- setName("AcceptThread" + mSocketType);
- BluetoothSocket socket = null;
- // Listen to the server socket if we're not connected
- while (mState != STATE_CONNECTED) {
- try {
- // This is a blocking call and will only return on a
- // successful connection or an exception
- //通过调用accept()来侦听连接请求。
- socket = mmServerSocket.accept();
- } catch (IOException e) {
- Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
- break;
- }
- // If a connection was accepted
- if (socket != null) {
- synchronized (BluetoothChatService.this) {
- switch (mState) {
- case STATE_LISTEN:
- case STATE_CONNECTING:
- // Situation normal. Start the connected thread.
- connected(socket, socket.getRemoteDevice(),
- mSocketType);
- break;
- case STATE_NONE:
- case STATE_CONNECTED:
- // Either not ready or already connected. Terminate new socket.
- try {
- socket.close();
- } catch (IOException e) {
- Log.e(TAG, "Could not close unwanted socket", e);
- }
- break;
- }
- }
- }
- }
- if (D) Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
- }
- public void cancel() {
- if (D) Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
- try {
- mmServerSocket.close();
- } catch (IOException e) {
- Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
- }
- }
- }
9、与远端设备通信
- //处理与远端的通信
- private class ConnectedThread extends Thread {
- private final BluetoothSocket mmSocket;
- private final InputStream mmInStream;
- private final OutputStream mmOutStream;
- public ConnectedThread(BluetoothSocket socket, String socketType) {
- Log.d(TAG, "create ConnectedThread: " + socketType);
- mmSocket = socket;
- InputStream tmpIn = null;
- OutputStream tmpOut = null;
- // Get the BluetoothSocket input and output streams
- try {
- tmpIn = socket.getInputStream();
- tmpOut = socket.getOutputStream();
- } catch (IOException e) {
- Log.e(TAG, "temp sockets not created", e);
- }
- mmInStream = tmpIn;
- mmOutStream = tmpOut;
- }
- public void run() {
- Log.i(TAG, "BEGIN mConnectedThread");
- byte[] buffer = new byte[1024];
- int bytes;
- // Keep listening to the InputStream while connected
- while (true) {
- try {
- // Read from the InputStream
- bytes = mmInStream.read(buffer);
- // Send the obtained bytes to the UI Activity
- mHandler.obtainMessage(BluetoothChat.MESSAGE_READ, bytes, -1, buffer)
- .sendToTarget();
- } catch (IOException e) {
- Log.e(TAG, "disconnected", e);
- connectionLost();
- // Start the service over to restart listening mode
- BluetoothChatService.this.start();
- break;
- }
- }
- }
- /**
- * Write to the connected OutStream.
- * @param buffer The bytes to write
- */
- //发送信息给远端
- public void write(byte[] buffer) {
- try {
- mmOutStream.write(buffer);
- // Share the sent message back to the UI Activity
- mHandler.obtainMessage(BluetoothChat.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);
- }
- }
- }