Android藍牙開發(fā)——實現(xiàn)藍牙聊天

最近課上剛好需要做一個課程設(shè)計關(guān)于藍牙的就挑選了個藍牙聊天室,其實關(guān)鍵還是在于對藍牙API的了解

一.藍牙API

與藍牙開發(fā)主要的相關(guān)類是以下四個

  • BluetoothAdapter
    字面上則理解為藍牙適配器,打開藍牙,關(guān)閉藍牙,搜索設(shè)備,獲取藍牙Socket連接都是通過這個類來實現(xiàn)的。對應(yīng)的方法就有
    • enable():用來打開藍牙,一般在5.0后的話則會出現(xiàn)彈框提示是否開啟藍牙,其他則沒有提示,也可以調(diào)用來進行彈窗打開
    Intent intent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivity(intent );
  • disable():關(guān)閉藍牙

  • getDefaultAdapter():獲取藍牙適配器BluetoothAdapter,只有這個方法來獲取

  • getAddress():獲取本地藍牙的MAC地址,這個則是每一個藍牙設(shè)備的唯一

  • getName():獲取本地藍牙的名稱

  • getRemoteDevice(String address):獲取藍牙地址獲取到遠(yuǎn)程的設(shè)備

  • startDiscovery():開啟藍牙設(shè)備搜索

  • cancelDiscovery():關(guān)閉掃描

  • listenUsingRfcommWithServiceRecord(serverSocketName,UUID):獲取BluetoothServerSocket

  • BluetoothDevice
    代表一個藍牙設(shè)備

    • createRfcommSocketToServiceRecord(UUIDuuid):根據(jù)UUID創(chuàng)建并返回一個BluetoothSocket

    • getName():獲取藍牙設(shè)備名稱

    • getAddress():獲取藍牙的MAC地址

  • BluetoothServerSocket:類似于ServerSocket,方法都差不多,可以說藍牙之間的通訊跟Socket相似。這個相當(dāng)于服務(wù)器Socket

  • accept():這個方法會阻塞,直到連接建立,用來監(jiān)聽連接

  • close():關(guān)閉socket連接

  • BluetoothSocket:相當(dāng)于客戶端Socket

    • connect():用來向服務(wù)器BluetoothServerSocket發(fā)起連接

    • getInputStream():獲取輸入流

    • getOutputStream():獲取輸出流

    • close():關(guān)閉連接

    • getRemoteDevice():獲取這個Socket連接的遠(yuǎn)程設(shè)備

二.搜索藍牙設(shè)備

知道對應(yīng)API后就可以進行對應(yīng)的藍牙開發(fā),這里以獲取藍牙設(shè)備為例子

1.獲取本地藍牙適配器
      BluetoothAdapter
mAdapter= BluetoothAdapter.getDefaultAdapter();
2.打開藍牙
 if(!mAdapter.isEnabled()){
//彈出對話框提示用戶是后打開
Intent enabler = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enabler, REQUEST_ENABLE);
      //不做提示,強行打開
// mAdapter.enable();

}

3.搜索設(shè)備
  mAdapter.startDiscovery(); //開啟搜索

搜索設(shè)備的回調(diào)則需要通過注冊廣播的形式來獲取

//發(fā)現(xiàn)設(shè)備的Action
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);  
registerReceiver(mReceiver, filter);  
//設(shè)備搜索完畢的action
filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);  
registerReceiver(mReceiver, filter);

定義廣播

BroadcastReceiver mReceiver = new BroadcastReceiver() {  
    public void onReceive(Context context, Intent intent) {  
        String action = intent.getAction();  
        //當(dāng)掃描到設(shè)備的時候
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // 獲取設(shè)備對象
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            //提取強度信息
            int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
            Log.e(TAG, device.getName() + "\n" + device.getAddress() + "\n強度:" + rssi);
          
        } //搜索完成
        else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
               Log.e(TAG, "onReceive: 搜索完成" );
        }
    }  
};  

之后就可以進行個人的一些操作

三.藍牙聊天的實現(xiàn)

要實現(xiàn)藍牙聊天則涉及到藍牙之間的傳輸通信,前面也說到了,這里肯定就是用到BluetoothServerSocket以及BluetoothSocket。

藍牙傳輸通信相當(dāng)于服務(wù)器端與客戶端之間的通信,只不過不同是這里每一個藍牙設(shè)備本身自己既充當(dāng)服務(wù)器端也充當(dāng)客戶端,大致的關(guān)系就是

藍牙連接

注意,這些連接都是阻塞式的,都要放在線程里去執(zhí)行。

1.對于BluetoothServerSocket,建立一個AcceptThread,來監(jiān)聽是否有設(shè)備發(fā)起連接
//連接等待線程
    class AcceptThread extends Thread{
        private final BluetoothServerSocket serverSocket;
        public AcceptThread(){
            BluetoothServerSocket tmp = null;
            try {
                    //獲取實例
                    tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE, MY_UUID_SECURE);
            }   catch (IOException e) {
                e.printStackTrace();
            }
            serverSocket = tmp;
        }
        @Override
        public void run() {
            super.run();
            //監(jiān)聽是否有端口連接
            BluetoothSocket socket = null;
            while(mState != STATE_TRANSFER) {
                try {
                    Log.e(TAG, "run: AcceptThread 阻塞調(diào)用,等待連接");
                    socket = serverSocket.accept();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "run: ActivityThread fail");
                    break;
                }
                //獲取到連接Socket后則開始通信
                if(socket != null){
                    synchronized (BluetoothChatService.this) {
                        switch (mState) {
                            case STATE_LISTEN:
                            case STATE_CONNECTING:
                                //傳輸數(shù)據(jù),服務(wù)器端調(diào)用
                                Log.e(TAG, "run: 服務(wù)器AcceptThread傳輸" );
                                sendMessageToUi(MainActivity.BLUE_TOOTH_DIALOG , "正在與" + socket.getRemoteDevice().getName() + "連接");
                                //開始數(shù)據(jù)傳輸
                                dataTransfer(socket, socket.getRemoteDevice());
                                break;
                            case STATE_NONE:
                            case STATE_TRANSFER:
                                // 沒有準(zhǔn)備好或者終止連接
                                try {
                                    socket.close();
                                } catch (IOException e) {
                                    Log.e(TAG, "Could not close unwanted socket" + e);
                                }
                                break;
                        }
                    }
                }
            }
        }

        public void cancel(){
            Log.e(TAG, "close: activity Thread" );
                try {
                    if(serverSocket != null)
                        serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "close: activity Thread fail");
                }
        }
    }

可以看到,當(dāng)BluetoothServerSocket監(jiān)聽到有設(shè)備連接的時候,就會調(diào)用dataTransfer開啟一個數(shù)據(jù)傳輸。

2.同樣如何發(fā)起連接BluetoothSocket呢

需要一個ConnectThread來發(fā)起

class ConnectThread extends Thread{
        private final BluetoothSocket socket;
        private final BluetoothDevice device;
        public ConnectThread(BluetoothDevice device) {
            this.device = device;
            BluetoothSocket mSocket = null;
            try {
                //獲取Socket
                mSocket = device.createRfcommSocketToServiceRecord(
                        MY_UUID_SECURE);
            } catch (IOException e) {
                e.printStackTrace();
                Log.e(TAG, "ConnectThread: fail" );
                sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "連接失敗,請重新連接");
            }
            socket = mSocket;
        }

        @Override
        public void run() {
            super.run();
            //建立后取消掃描
            bluetoothAdapter.cancelDiscovery();

            try {
                //開啟連接
                socket.connect();
            } catch (IOException e) {
                e.printStackTrace();
                try {
                    socket.close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                    Log.e(TAG, "run: unable to close" );
                }
                //TODO 連接失敗顯示
                sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "連接失敗,請重新連接");
                BluetoothChatService.this.start();
            }


            // 重置
            synchronized (BluetoothChatService.this) {
                mConnectThread = null;
            }
            //連接建立,開始傳輸
            dataTransfer(socket, device);
        }

        public void cancel(){
                try {
                    if(socket != null)
                        socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    }

之后建立連接之后就會調(diào)用dataTransfer來進行數(shù)據(jù)傳輸,同樣也需要一個線程來維護數(shù)據(jù)傳輸

class TransferThread extends Thread{
        private final BluetoothSocket socket;
        private final OutputStream out;
        private final InputStream in;
        public TransferThread(BluetoothSocket mBluetoothSocket){
                socket = mBluetoothSocket;
                OutputStream mOutputStream = null;
                InputStream mInputStream = null;
                try {
                    if(socket != null){
                        //獲取連接的輸入輸出流
                        mOutputStream = socket.getOutputStream();
                        mInputStream = socket.getInputStream();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
                out = mOutputStream;
                in = mInputStream;
                isTransferError = false;
        }
        @Override
        public void run() {
            super.run();
            //讀取數(shù)據(jù)
            byte[] buffer = new byte[1024];
            int bytes;
            while (true){
                try {
                    bytes = in.read(buffer);
                    //TODO 分發(fā)到主線程顯示
                    uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_READ, bytes, -1, buffer)
                            .sendToTarget();
                    Log.e(TAG, "run: read " + new String(buffer , 0 , bytes) );
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "run: Transform error"  + e.toString());
                    BluetoothChatService.this.start();
                    //TODO 連接丟失顯示并重新開始連接
                    sendMessageToUi(MainActivity.BLUE_TOOTH_TOAST , "設(shè)備連接失敗/傳輸關(guān)閉");
                    isTransferError = true;
                    break;
                }
            }
        }

        /**
         * 寫入數(shù)據(jù)傳輸
         * @param buffer
         */
        public void write(byte[] buffer) {
            try {
                out.write(buffer);
                //TODO 到到UI顯示
                uiHandler.obtainMessage(MainActivity.BLUE_TOOTH_WRAITE , -1, -1, buffer)
                        .sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Exception during write " + e);
            }
        }

        public void cancel() {
            try {
                if(socket != null)
                    socket.close();
            } catch (IOException e) {
                Log.e(TAG, "close() of connect socket failed" + e);
            }
        }
    }

藍牙聊天則是基于上面三個線程來進行實現(xiàn),同樣,對于藍牙文件間的傳輸也是同個道理,通過輸入輸出流來進行處理。之后的操作就比較容易處理了

四.簡單實現(xiàn)

項目代碼

藍牙聊天

五.參考鏈接

Android 藍牙開發(fā)基本流程

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容