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連接都是通過(guò)這個(gè)類來(lái)實(shí)現(xiàn)的五慈。對(duì)應(yīng)的方法就有
    • enable():用來(lái)打開藍(lán)牙,一般在5.0后的話則會(huì)出現(xiàn)彈框提示是否開啟藍(lán)牙衫哥,其他則沒(méi)有提示,也可以調(diào)用來(lái)進(jìn)行彈窗打開
    Intent intent =new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivity(intent );
  • disable():關(guān)閉藍(lán)牙

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

  • 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,方法都差不多粮坞,可以說(shuō)藍(lán)牙之間的通訊跟Socket相似蚊荣。這個(gè)相當(dāng)于服務(wù)器Socket

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

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

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

    • connect():用來(lái)向服務(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)則需要通過(guò)注冊(cè)廣播的形式來(lái)獲取

//發(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)牙之間的傳輸通信筝闹,前面也說(shuō)到了敲霍,這里肯定就是用到BluetoothServerSocket以及BluetoothSocket俊马。

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

藍(lán)牙連接

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

1.對(duì)于BluetoothServerSocket艘儒,建立一個(gè)AcceptThread,來(lái)監(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:
                                // 沒(méi)有準(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來(lái)發(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來(lái)進(jìn)行數(shù)據(jù)傳輸融欧,同樣也需要一個(gè)線程來(lái)維護(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è)線程來(lái)進(jìn)行實(shí)現(xiàn)葛假,同樣,對(duì)于藍(lán)牙文件間的傳輸也是同個(gè)道理抡砂,通過(guò)輸入輸出流來(lái)進(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)系作者
  • 序言:七十年代末窒篱,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子舶沿,更是在濱河造成了極大的恐慌墙杯,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件括荡,死亡現(xiàn)場(chǎng)離奇詭異高镐,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)一汽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門避消,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人召夹,你說(shuō)我怎么就攤上這事岩喷。” “怎么了监憎?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵纱意,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我鲸阔,道長(zhǎng)偷霉,這世上最難降的妖魔是什么迄委? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮类少,結(jié)果婚禮上叙身,老公的妹妹穿的比我還像新娘。我一直安慰自己硫狞,他們只是感情好信轿,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著残吩,像睡著了一般财忽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泣侮,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天即彪,我揣著相機(jī)與錄音,去河邊找鬼活尊。 笑死隶校,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的酬凳。 我是一名探鬼主播惠况,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼遭庶,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼宁仔!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起峦睡,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤翎苫,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后榨了,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體煎谍,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年龙屉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了呐粘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡转捕,死狀恐怖作岖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情五芝,我是刑警寧澤痘儡,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站枢步,受9級(jí)特大地震影響沉删,放射性物質(zhì)發(fā)生泄漏渐尿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一矾瑰、第九天 我趴在偏房一處隱蔽的房頂上張望砖茸。 院中可真熱鬧,春花似錦殴穴、人聲如沸渔彰。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)恍涂。三九已至,卻和暖如春植榕,著一層夾襖步出監(jiān)牢的瞬間再沧,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工尊残, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留炒瘸,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓寝衫,卻偏偏與公主長(zhǎng)得像顷扩,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子慰毅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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