2013.09.05——— android 蓝牙聊天室之官方例子

蓝牙开发的大致流程:

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);            }        }    }



附件是官网的例子,稍微加了点注释

更多相关文章

  1. Android设备功能之传感器教程篇
  2. android aidl 进程通信 调用错误
  3. Android 利用TCP通信 实现环境数据显示及控制指令下发
  4. Android Wear带你理解跨设备的Android 技术体系
  5. Android获取设备信息
  6. Android USB通信
  7. RN系列:Android原生与RN如何交互通信
  8. Android 9.0 Launcher源码分析(三)——Launcher的布局与多设备适配
  9. Android跨进程通信IPC系列

随机推荐

  1. Android使用通讯录的权限问题
  2. android SmartTabLayout——自定义标题带
  3. Android -----listView的属性大全
  4. android 异常 汇总
  5. android常见编译错误
  6. AndroidAZ系列:ContentProvider(All,Face
  7. Android(安卓)还可以走多久?
  8. Android dependency 'android.arch.core:
  9. Android下载 文件(APP) 并且静默安装
  10. Kotlin概述之Kotlin for Android