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

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

一.藍(lán)牙API

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

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

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

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

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

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

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

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

  • listenUsingRfcommWithServiceRecord(serverSocketName,UUID):獲取BluetoothServerSocket

  • BluetoothDevice
    代表一個(gè)藍(lán)牙設(shè)備

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

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

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

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

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

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

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

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

    • getInputStream():獲取輸入流

    • getOutputStream():獲取輸出流

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

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

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

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

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

}

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

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

//發(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è)備的時(shí)候
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // 獲取設(shè)備對(duì)象
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            //提取強(qiáng)度信息
            int rssi = intent.getExtras().getShort(BluetoothDevice.EXTRA_RSSI);
            Log.e(TAG, device.getName() + "\n" + device.getAddress() + "\n強(qiáng)度:" + rssi);
          
        } //搜索完成
        else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
               Log.e(TAG, "onReceive: 搜索完成" );
        }
    }  
};  

之后就可以進(jìn)行個(gè)人的一些操作

三.藍(lán)牙聊天的實(shí)現(xiàn)

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

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

藍(lán)牙連接

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

1.對(duì)于BluetoothServerSocket,建立一個(gè)AcceptThread,來監(jiān)聽是否有設(shè)備發(fā)起連接
//連接等待線程
    class AcceptThread extends Thread{
        private final BluetoothServerSocket serverSocket;
        public AcceptThread(){
            BluetoothServerSocket tmp = null;
            try {
                    //獲取實(shí)例
                    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è)備連接的時(shí)候,就會(huì)調(diào)用dataTransfer開啟一個(gè)數(shù)據(jù)傳輸。

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

需要一個(gè)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 , "連接失敗,請(qǐng)重新連接");
            }
            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 , "連接失敗,請(qǐng)重新連接");
                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();
                }
        }
    }

之后建立連接之后就會(huì)調(diào)用dataTransfer來進(jìn)行數(shù)據(jù)傳輸,同樣也需要一個(gè)線程來維護(hù)數(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);
            }
        }
    }

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

四.簡(jiǎn)單實(shí)現(xiàn)

項(xiàng)目代碼

藍(lán)牙聊天

五.參考鏈接

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容