Android藍牙開發——實現藍牙聊天

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

一.藍牙API

與藍牙開發主要的相關類是以下四個

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

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

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

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

  • getRemoteDevice(String address):獲取藍牙地址獲取到遠程的設備

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

  • cancelDiscovery():關閉掃描

  • listenUsingRfcommWithServiceRecord(serverSocketName,UUID):獲取BluetoothServerSocket

  • BluetoothDevice
    代表一個藍牙設備

    • createRfcommSocketToServiceRecord(UUIDuuid):根據UUID創建并返回一個BluetoothSocket

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

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

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

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

  • close():關閉socket連接

  • BluetoothSocket:相當于客戶端Socket

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

    • getInputStream():獲取輸入流

    • getOutputStream():獲取輸出流

    • close():關閉連接

    • getRemoteDevice():獲取這個Socket連接的遠程設備

二.搜索藍牙設備

知道對應API后就可以進行對應的藍牙開發,這里以獲取藍牙設備為例子

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

}

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

搜索設備的回調則需要通過注冊廣播的形式來獲取

//發現設備的Action
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);  
registerReceiver(mReceiver, filter);  
//設備搜索完畢的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();  
        //當掃描到設備的時候
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
           // 獲取設備對象
            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: 搜索完成" );
        }
    }  
};  

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

三.藍牙聊天的實現

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

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

藍牙連接

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

1.對于BluetoothServerSocket,建立一個AcceptThread,來監聽是否有設備發起連接
//連接等待線程
    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();
            //監聽是否有端口連接
            BluetoothSocket socket = null;
            while(mState != STATE_TRANSFER) {
                try {
                    Log.e(TAG, "run: AcceptThread 阻塞調用,等待連接");
                    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:
                                //傳輸數據,服務器端調用
                                Log.e(TAG, "run: 服務器AcceptThread傳輸" );
                                sendMessageToUi(MainActivity.BLUE_TOOTH_DIALOG , "正在與" + socket.getRemoteDevice().getName() + "連接");
                                //開始數據傳輸
                                dataTransfer(socket, socket.getRemoteDevice());
                                break;
                            case STATE_NONE:
                            case STATE_TRANSFER:
                                // 沒有準備好或者終止連接
                                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");
                }
        }
    }

可以看到,當BluetoothServerSocket監聽到有設備連接的時候,就會調用dataTransfer開啟一個數據傳輸。

2.同樣如何發起連接BluetoothSocket呢

需要一個ConnectThread來發起

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

之后建立連接之后就會調用dataTransfer來進行數據傳輸,同樣也需要一個線程來維護數據傳輸

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();
            //讀取數據
            byte[] buffer = new byte[1024];
            int bytes;
            while (true){
                try {
                    bytes = in.read(buffer);
                    //TODO 分發到主線程顯示
                    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 , "設備連接失敗/傳輸關閉");
                    isTransferError = true;
                    break;
                }
            }
        }

        /**
         * 寫入數據傳輸
         * @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);
            }
        }
    }

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

四.簡單實現

項目代碼

藍牙聊天

五.參考鏈接

Android 藍牙開發基本流程

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容