Android-BLE藍(lán)牙開(kāi)發(fā)學(xué)習(xí)記錄

Android開(kāi)發(fā)并非我擅長(zhǎng)偿警,最近剛好又做回藍(lán)牙業(yè)務(wù)了负间,iOS搞完了看了下Android的,似乎區(qū)別不大隙姿,唯一比iOS多了一個(gè)descriptor梅垄,那就順便把Android藍(lán)牙也搞搞吧,本文僅做學(xué)習(xí)記錄完成過(guò)程以及中途遇到的坑。代碼是否規(guī)范不重要哈队丝!

1.申請(qǐng)權(quán)限

首先在第一步我就遇到了坑靡馁,Android藍(lán)牙開(kāi)發(fā)需要申請(qǐng)地理位置權(quán)限,位置權(quán)限是敏感權(quán)限机久,高版本Android需要?jiǎng)討B(tài)申請(qǐng)臭墨,最開(kāi)始我只在AndroidManifest添加了權(quán)限申請(qǐng),結(jié)果就是不管用舊的API還是最新的API掃描膘盖,都不走掃描結(jié)果回調(diào)胧弛!注意避坑!O琅稀结缚!
AndroidManifest中申請(qǐng)以下權(quán)限

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

    <uses-feature android:name="android.hardware.location.gps" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>

然后在代碼中動(dòng)態(tài)申請(qǐng),這里寫一個(gè)requestPermission方法申請(qǐng)

 private void requestPermission() {
        //動(dòng)態(tài)申請(qǐng)
        if (Build.VERSION.SDK_INT < 23) {
            return;
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
        }
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
        }
    }

2.初始化藍(lán)牙

  private void initBluetooth() {
        mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
        if (mBluetoothAdapter == null) {
            Log.d(TAG, "藍(lán)牙不支持");
        } else {
            int status = mBluetoothAdapter.getState();
            if (status == BluetoothAdapter.STATE_OFF) {
                mBluetoothAdapter.enable();
            }else {
                Log.d(TAG, "藍(lán)牙可用");
            }
        }
    }

3.開(kāi)始掃描

掃描的方式可以用BluetoothAdapter.startLeScan掃描软棺,但谷歌已經(jīng)不建議用該方法掃描了红竭,那就用最新的BluetoothLeScanner的方式掃描吧,在需要掃描的地方添加下面的代碼喘落,掃描可以根據(jù)服務(wù)UUID掃描固定一類藍(lán)牙設(shè)備茵宪,也可以不過(guò)濾掃描所有設(shè)備,我這里根據(jù)服務(wù)UUID過(guò)濾(Android除了UUID過(guò)濾掃描瘦棋,還有其他的稀火,比如name等,沒(méi)仔細(xì)研究赌朋,iOS過(guò)濾條件目前就只有UUID)

        List<ScanFilter> bleScanFilters = new ArrayList<>();
        bleScanFilters.add(new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("你的藍(lán)牙設(shè)備UUID")).build());
        ScanSettings scanSetting = new ScanSettings.Builder().setScanMode(SCAN_MODE_LOW_LATENCY).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).setMatchMode(ScanSettings.MATCH_MODE_STICKY).build();
        mBluetoothLeScanner.startScan(bleScanFilters, scanSetting, callback);
        Log.d(TAG, "開(kāi)始掃描");

4.掃描回調(diào)并連接

因?yàn)槲沂歉鶕?jù)UUID只掃描一類設(shè)備凰狞,為了省電掃描到后我直接停止掃描,然后連接第一個(gè)掃描到的設(shè)備

final ScanCallback stopCallback = new ScanCallback() {
    };

final ScanCallback callback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);
            Log.d(TAG, "掃描到設(shè)備");
            BluetoothDevice btDevice = result.getDevice();
            mBluetoothLeScanner.stopScan(stopCallback);
            btDevice.connectGatt(MainActivity.this,true, connectCallBack);
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
            super.onBatchScanResults(results);
            Log.d(TAG, "onBatchScanResults");
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);
            Log.d(TAG, "掃描出錯(cuò)");
        }
    };

5.連接回調(diào)箕慧,發(fā)現(xiàn)服務(wù)服球,發(fā)現(xiàn)特征值,監(jiān)聽(tīng)藍(lán)牙發(fā)過(guò)來(lái)的數(shù)據(jù)颠焦,發(fā)送數(shù)據(jù)

    final  BluetoothGattCallback connectCallBack = new BluetoothGattCallback(){
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            super.onConnectionStateChange(gatt, status, newState);
            if (newState == BluetoothProfile.STATE_CONNECTED){
                Log.d(TAG, "連接成功");
                gatt.discoverServices();
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            super.onServicesDiscovered(gatt, status);
            Log.d(TAG, "發(fā)現(xiàn)服務(wù)");
            BluetoothGattService service = gatt.getService(UUID.fromString("藍(lán)牙設(shè)備服務(wù)uuid"));
            if (service != null){
                Log.d(TAG, "有服務(wù)");
                BluetoothGattCharacteristic notyChara = service.getCharacteristic(UUID.fromString("藍(lán)牙設(shè)備可監(jiān)聽(tīng)征值uuid"));
                if (notyChara != null){
                    Log.d(TAG, "有特征值");
                    boolean success = gatt.setCharacteristicNotification(notyChara,true);
                    Log.i("監(jiān)聽(tīng)結(jié)果: "+success);
                    BluetoothGattDescriptor descriptor = notyChara.getDescriptor(UUID.fromString("藍(lán)牙設(shè)備描述uuid"));
                    if (descriptor != null){
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
                        gatt.writeDescriptor(descriptor);
                    }
                }
                //發(fā)送數(shù)據(jù)
                BluetoothGattCharacteristic writeChar = service.getCharacteristic(UUID.fromString("藍(lán)牙設(shè)備可寫特征值的UUID"));
                writeChar.setValue(TAG.getBytes());
                gatt.writeCharacteristic(writeChar);
            }
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            super.onCharacteristicChanged(gatt, characteristic);
            Log.d(TAG, "收到數(shù)據(jù)");
        }
    };

值得一提的是斩熊,為了省電,在不需要監(jiān)聽(tīng)數(shù)據(jù)后需要設(shè)置停止監(jiān)聽(tīng)伐庭,也可以用readCharacteristic讀取單條數(shù)據(jù)

gatt.setCharacteristicNotification(chara,false);
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);

在不需要藍(lán)牙后應(yīng)該調(diào)用gatt的disconnect方法斷開(kāi)設(shè)備粉渠,因?yàn)橛凶畲筮B接數(shù)限制,還需要調(diào)用gatt的close方法釋放當(dāng)前連接個(gè)數(shù)計(jì)數(shù)圾另。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末霸株,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子集乔,更是在濱河造成了極大的恐慌去件,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異尤溜,居然都是意外死亡倔叼,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門宫莱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)丈攒,“玉大人,你說(shuō)我怎么就攤上這事授霸⊙惭椋” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,445評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵碘耳,是天一觀的道長(zhǎng)显设。 經(jīng)常有香客問(wèn)我,道長(zhǎng)藏畅,這世上最難降的妖魔是什么敷硅? 我笑而不...
    開(kāi)封第一講書人閱讀 55,185評(píng)論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮愉阎,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘力奋。我一直安慰自己榜旦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,178評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布景殷。 她就那樣靜靜地躺著溅呢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪猿挚。 梳的紋絲不亂的頭發(fā)上咐旧,一...
    開(kāi)封第一講書人閱讀 48,970評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音绩蜻,去河邊找鬼铣墨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛办绝,可吹牛的內(nèi)容都是我干的伊约。 我是一名探鬼主播,決...
    沈念sama閱讀 38,276評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼孕蝉,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼屡律!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起降淮,我...
    開(kāi)封第一講書人閱讀 36,927評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤超埋,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體霍殴,經(jīng)...
    沈念sama閱讀 43,400評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡媒惕,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,883評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了繁成。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片吓笙。...
    茶點(diǎn)故事閱讀 37,997評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖巾腕,靈堂內(nèi)的尸體忽然破棺而出面睛,到底是詐尸還是另有隱情,我是刑警寧澤尊搬,帶...
    沈念sama閱讀 33,646評(píng)論 4 322
  • 正文 年R本政府宣布叁鉴,位于F島的核電站,受9級(jí)特大地震影響佛寿,放射性物質(zhì)發(fā)生泄漏幌墓。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,213評(píng)論 3 307
  • 文/蒙蒙 一冀泻、第九天 我趴在偏房一處隱蔽的房頂上張望常侣。 院中可真熱鬧,春花似錦弹渔、人聲如沸胳施。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,204評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)舞肆。三九已至,卻和暖如春博杖,著一層夾襖步出監(jiān)牢的瞬間椿胯,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,423評(píng)論 1 260
  • 我被黑心中介騙來(lái)泰國(guó)打工剃根, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哩盲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,423評(píng)論 2 352
  • 正文 我出身青樓跟继,卻偏偏與公主長(zhǎng)得像种冬,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子舔糖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,722評(píng)論 2 345

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