Android 藍(lán)牙搜索,配對鞭衩,連接發(fā)送數(shù)據(jù)

首先需要在清單配置里面添加兩個權(quán)限:

<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

android里面藍(lán)牙是通過BluetoothAdapter來進(jìn)行操作的,所以首先我們需要獲取到BluetoothAdapter的實(shí)例

//先獲取BlueToothAdapter的實(shí)例
BluetoothAdapter blueToothAdapter = BluetoothAdapter.getDefaultAdapter();

在搜索之前娃善,我們可以先獲取與我們配對過的設(shè)備

//獲取已經(jīng)配對過的設(shè)備的集合
Set<BluetoothDevice> bondedDevices = blueToothAdapter.getBondedDevices()

但是想要獲取到配對過的設(shè)備论衍,我們必須是在

//手機(jī)有藍(lán)牙設(shè)備并且藍(lán)牙是打開的
blueToothAdapter != null && blueToothAdapter.isEnabled()

下面我們談一下藍(lán)牙的<b>打開方式</b>,目前作者知道的方式有3種
第一種:

//強(qiáng)制打開藍(lán)牙
blueToothAdapter.enable();

第二種:

//會以dialog的形式打開一個activity聚磺,并且如果我們通過startActivityForResult的形式的話
//還能查看藍(lán)牙是否被打開坯台,或者處理藍(lán)牙被打開之后的操作
//如果是result_ok的話那么是打開,反之打開失敗
startActivityForResult(new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE));

第三種:

//設(shè)置本地設(shè)備可以被其它設(shè)備搜索瘫寝,可被搜索的時間是有限的捂人,最多為300s
//效果和第二種類似
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);

<b>關(guān)閉藍(lán)牙</b>我們可以調(diào)用

blueToothAdapter.disable();

接下來是<b>藍(lán)牙搜索</b>
首先如果想要開啟藍(lán)牙搜索,那么只需要調(diào)用

blueToothAdapter.startDiscovery();

但是我們該如何接收我們搜索到的設(shè)備呢矢沿,很明顯當(dāng)然是通過接收廣播的形式來接收滥搭。所以我們應(yīng)該自定義一個廣播接收器,

class BluetoothReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {

            if (intent.getAction().equals(BluetoothDevice.ACTION_FOUND)) {
                Log.e(getPackageName(), "找到新設(shè)備了");
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            }
        }
    }

如上代碼捣鲸,我們可以獲取到搜索到的藍(lán)牙設(shè)備瑟匆。我們只需在代碼里面注冊這個廣播接收器,在調(diào)用藍(lán)牙的搜索方法栽惶,就能夠進(jìn)行藍(lán)牙的搜索了愁溜。

//注冊廣播
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
registerReceiver(new BluetoothReceiver(), intentFilter);

這里需要注意的是,如果你的代碼將運(yùn)行在(Build.VERSION.SDK_INT >= 23)的設(shè)備上外厂,那么務(wù)必加上以下權(quán)限冕象,并在代碼中動態(tài)的申請權(quán)限

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
private void requestPermission() {
        if (Build.VERSION.SDK_INT >= 23) {
            int checkAccessFinePermission = ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
            if (checkAccessFinePermission != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                        REQUEST_PERMISSION_ACCESS_LOCATION);
                Log.e(getPackageName(), "沒有權(quán)限,請求權(quán)限");
                return;
            }
            Log.e(getPackageName(), "已有定位權(quán)限");
            //這里可以開始搜索操作
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
        switch (requestCode) {
            case REQUEST_PERMISSION_ACCESS_LOCATION: {
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.e(getPackageName(), "開啟權(quán)限permission granted!");
                    //這里可以開始搜索操作
                } else {
                    Log.e(getPackageName(), "沒有定位權(quán)限汁蝶,請先開啟!");
                }
            }
        }
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }

再來就是<b>藍(lán)牙的配對</b>渐扮,目前樓主采用的是通過反射的形式來調(diào)用BluetoothDevice的createBondde()方法论悴,如果我們想監(jiān)聽配對的這個過程,那么我們可以為廣播接收器再注冊一個action墓律。

try {
      //如果想要取消已經(jīng)配對的設(shè)備膀估,只需要將creatBond改為removeBond
       Method method = BluetoothDevice.class.getMethod("createBond");
       Log.e(getPackageName(), "開始配對");
       method.invoke(device);
    } catch (Exception e) {
      e.printStackTrace();
    }
//綁定狀態(tài)發(fā)生變化
intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED);

廣播接收器里面我們可以這樣寫

if (intent.getAction().equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
                BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
                switch (device.getBondState()) {
                    case BluetoothDevice.BOND_NONE:
                        Log.e(getPackageName(), "取消配對");
                        break;
                    case BluetoothDevice.BOND_BONDING:
                        Log.e(getPackageName(), "配對中");
                        break;
                    case BluetoothDevice.BOND_BONDED:
                        Log.e(getPackageName(), "配對成功");
                        break;
                }
}

再來就是<b>向已經(jīng)配對的設(shè)備發(fā)送數(shù)據(jù)</b>
發(fā)送數(shù)據(jù)分為服務(wù)端和客戶端,通過socket來進(jìn)行消息的交互耻讽。

服務(wù)端

new Thread(new Runnable() {
            @Override
            public void run() {
                InputStream is = null;
                try {
                    BluetoothServerSocket serverSocket = blueToothAdapter.listenUsingRfcommWithServiceRecord("serverSocket", uuid);
                    mHandler.sendEmptyMessage(startService);
                    BluetoothSocket accept = serverSocket.accept();
                    is = accept.getInputStream();

                    byte[] bytes = new byte[1024];
                    int length = is.read(bytes);

                    Message msg = new Message();
                    msg.what = getMessageOk;
                    msg.obj = new String(bytes, 0, length);
                    mHandler.sendMessage(msg);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();

客戶端

new Thread(new Runnable() {
            @Override
            public void run() {
                OutputStream os = null;
                try {
                    BluetoothSocket socket = strArr.get(i).createRfcommSocketToServiceRecord(uuid);
                    socket.connect();
                    os = socket.getOutputStream();
                    os.write("testMessage".getBytes());
                    os.flush();
                    mHandler.sendEmptyMessage(sendOver);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();

可以看到無論是服務(wù)端還是客戶端察纯,都需要新起一個子線程來操作。那么服務(wù)端和客戶端是怎么識別對方的呢针肥,那么就需要用到<b>UUID</b>了饼记,只有當(dāng)服務(wù)端和客戶端的<b>UUID</b>相同的時候才能夠建立連接。

<b>藍(lán)牙連接發(fā)送數(shù)據(jù)的時候的坑</b>
當(dāng)博主第一次寫好客戶端服務(wù)端測試的時候慰枕,服務(wù)端一直報(bào)錯

java.io.IOException: bt socket closed, read return: -1

也就是這句話報(bào)錯

is.read(bytes)

當(dāng)時一直以為是讀取返回值是-1就會報(bào)錯具则,但是不對啊,以前這么寫也沒錯過捺僻,在網(wǎng)上百度了半天,看了別人的博客論壇也沒有解決辦法崇裁,最后才注意到報(bào)錯的前一句匕坯,

bt socket closed

之前一直認(rèn)為是什么服務(wù)端這邊,報(bào)錯連接才會斷開拔稳,后來一想客戶端也會斷開連接啊葛峻,這才找到問題所在。原來是我當(dāng)時write完數(shù)據(jù)之后將連接關(guān)閉了巴比,這才導(dǎo)致服務(wù)端這邊連接斷開的术奖。所以重要的事情要說3遍,千萬別急著斷開連接轻绞,千萬別急著斷開連接采记,千萬別急著斷開連接。

<a >github源代碼鏈接</a>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末政勃,一起剝皮案震驚了整個濱河市唧龄,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奸远,老刑警劉巖既棺,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異懒叛,居然都是意外死亡丸冕,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門薛窥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胖烛,“玉大人,你說我怎么就攤上這事『榧海” “怎么了妥凳?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長答捕。 經(jīng)常有香客問我逝钥,道長,這世上最難降的妖魔是什么拱镐? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任艘款,我火速辦了婚禮,結(jié)果婚禮上沃琅,老公的妹妹穿的比我還像新娘哗咆。我一直安慰自己,他們只是感情好益眉,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布晌柬。 她就那樣靜靜地躺著,像睡著了一般郭脂。 火紅的嫁衣襯著肌膚如雪年碘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天展鸡,我揣著相機(jī)與錄音屿衅,去河邊找鬼。 笑死莹弊,一個胖子當(dāng)著我的面吹牛涤久,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播忍弛,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼响迂,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了细疚?” 一聲冷哼從身側(cè)響起栓拜,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎惠昔,沒想到半個月后幕与,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡镇防,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年啦鸣,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片来氧。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡诫给,死狀恐怖香拉,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情中狂,我是刑警寧澤凫碌,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站胃榕,受9級特大地震影響盛险,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜勋又,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一苦掘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧楔壤,春花似錦鹤啡、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至隙畜,卻和暖如春抖部,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背禾蚕。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工您朽, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留狂丝,地道東北人换淆。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像几颜,于是被迫代替她去往敵國和親倍试。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

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