Android 藍(lán)牙BLE開發(fā)從官方源碼demo開始(二)

一思犁、前言

在上一篇文章Android 藍(lán)牙BLE開發(fā)從官方源碼demo開始(一)我們已經(jīng)看了官方的demo幔欧,知道了怎么開始配置Android藍(lán)牙4.0包警,并且也成功地進(jìn)行掃描并獲取回調(diào)的藍(lán)牙設(shè)備參數(shù),然后對參數(shù)進(jìn)行處理展示伯顶,其中第一個(gè)參數(shù)device,表示一個(gè)遠(yuǎn)程藍(lán)牙設(shè)備骆膝,里面有它獨(dú)有的藍(lán)牙地址Address和Name祭衩;我們要拿到這個(gè)設(shè)備Address進(jìn)行藍(lán)牙連接和讀寫操作。

谷歌給我們提供了官方源碼demo:
https://github.com/googlesamples/android-BluetoothLeGatt
接下來我們繼續(xù)來學(xué)習(xí)谷歌官方給我們提供的藍(lán)牙BLE源碼

二阅签、創(chuàng)建BluetoothLeService服務(wù)類并初始化藍(lán)牙連接

在官方demo中掐暮,藍(lán)牙ble的連接和讀寫操作都是在DeviceControlActivity中實(shí)現(xiàn),可以下載demo源碼政钟,編譯運(yùn)行一遍路克!
來到此Activity,我們先看onCreate()方法可知养交,程序先執(zhí)行bindService開啟了一個(gè)服務(wù)

 Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
 bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);

并在服務(wù)回調(diào)已經(jīng)成功連接時(shí)精算,獲取了BlueToohtLeService的實(shí)例,接著就執(zhí)行藍(lán)牙連接操作:

    // Code to manage Service lifecycle.
    private final ServiceConnection mServiceConnection = new ServiceConnection() {
 
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder service) {
            mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
            if (!mBluetoothLeService.initialize()) {
                Log.e(TAG, "Unable to initialize Bluetooth");
                finish();
            }
            // Automatically connects to the device upon successful start-up initialization.
            mBluetoothLeService.connect(mDeviceAddress);
        }
 
        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            mBluetoothLeService = null;
        }
    };

這個(gè)BlueToohtLeService類既然是服務(wù)類碎连,那它父類肯定是繼承于Service灰羽;接著實(shí)現(xiàn)了Service的先進(jìn)入onBind()方法;
1.onBind()是使用bindService開啟的服務(wù)才會(huì)有回調(diào)的一個(gè)方法鱼辙。
這里官方demo在onBind()方法給我們的Activity返回了BluetoothLeService實(shí)例廉嚼,方便Activity后續(xù)的連接和讀寫操作;

    private final IBinder mBinder = new LocalBinder();
 
    public class LocalBinder extends Binder {
        BluetoothLeService getService() {
            return BluetoothLeService.this;
        }
    }
 
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

2.當(dāng)服務(wù)調(diào)用unbindService時(shí)座每,服務(wù)的生命周期將會(huì)進(jìn)入onUnbind()方法前鹅;接著執(zhí)行了關(guān)閉藍(lán)牙的方法;

    @Override
    public boolean onUnbind(Intent intent) {
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        close();
        return super.onUnbind(intent);
    }

3.initialize() 初始化藍(lán)牙適配器峭梳;接著在這demo里這個(gè)方法是在服務(wù)建立后在Activity通過拿到BlueToohtLeService實(shí)例調(diào)用的舰绘。

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    public boolean initialize() {
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                Log.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }
 
        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }
 
        return true;
    }

4.connect()方法 傳入藍(lán)牙地址進(jìn)行連接藍(lán)牙操作;先判斷藍(lán)牙適配器是否為空葱椭,然后判斷是否剛斷開需要重連的設(shè)備捂寿,否則就通過藍(lán)牙適配器獲取BluetoothGatt實(shí)例去連接藍(lán)牙操作,后續(xù)還會(huì)使用到BluetoothGatt去讀寫操作和斷開孵运、關(guān)閉操作秦陋;

    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }
 
        // Previously connected device.  Try to reconnect.
        // 以前連接的設(shè)備。 嘗試重新連接治笨。
        if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
                && mBluetoothGatt != null) {
            Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
            if (mBluetoothGatt.connect()) {
                mConnectionState = STATE_CONNECTING;
                return true;
            } else {
                return false;
            }
        }
 
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            Log.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect 我們想直接連接到設(shè)備驳概,因此我們設(shè)置autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        Log.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

5.BluetoothGattCallback 回調(diào)赤嚼;這個(gè)回調(diào)可以說很重要,核心部分顺又,主要對BluetoothGatt的藍(lán)牙連接更卒、斷開、讀稚照、寫蹂空、特征值變化等的回調(diào)監(jiān)聽,然后我們可以將這些回調(diào)信息通過廣播機(jī)制傳播回給廣播監(jiān)聽器果录。

    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
 
        }
 
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
 
        }
 
        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
        }
 
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
 
        }
    };

三上枕、廣播監(jiān)聽器

在這個(gè)官方demo中,就是使用了廣播來作為activity和service之間的數(shù)據(jù)傳遞弱恒;繼續(xù)回到源碼:activity開啟前面所說的服務(wù)之后辨萍,就注冊了這個(gè)mGattUpdateReceiver廣播;

    registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
        if (mBluetoothLeService != null) {
            final boolean result = mBluetoothLeService.connect(mDeviceAddress);
            Log.d(TAG, "Connect request result=" + result) ;
        }
   private static IntentFilter makeGattUpdateIntentFilter() {
        final IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
        intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
        intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
        return intentFilter;
    }

關(guān)于這個(gè)廣播的回調(diào)監(jiān)聽如下:有注釋就不多解釋了返弹,它的作用就是接收從service發(fā)送回來的信息分瘦;上文有說到BluetoothGattCallback,就是從這里發(fā)送廣播的琉苇。

   // Handles various events fired by the Service. 處理服務(wù)部門發(fā)起的各種事件
    // ACTION_GATT_CONNECTED: connected to a GATT server. 連接到GATT服務(wù)器嘲玫。
    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server. 與GATT服務(wù)器斷開連接。
    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.發(fā)現(xiàn)GATT服務(wù)
    // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
    //                        or notification operations. 從設(shè)備接收數(shù)據(jù)并扇。 這可能是閱讀的結(jié)果
    //     //或通知操作去团。
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                mConnected = true;
                updateConnectionState(R.string.connected);
                invalidateOptionsMenu();
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                mConnected = false;
                updateConnectionState(R.string.disconnected);
                invalidateOptionsMenu();
                clearUI();
            } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // Show all the supported services and characteristics on the user interface.
                displayGattServices(mBluetoothLeService.getSupportedGattServices());
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
            }
        }
    };

這里留意一下:當(dāng)連接成功后,首先service那邊會(huì)發(fā)現(xiàn)服務(wù)特征值穷蛹,通過廣播傳輸回來土陪,然后執(zhí)行下面的方法:

   displayGattServices(mBluetoothLeService.getSupportedGattServices());

四、其他方法

  1. setCharacteristicNotification()肴熏;調(diào)用此方法開啟特征值的通知鬼雀;
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

2.開啟讀,我們可以用如下方法蛙吏,但是此方法有個(gè)缺點(diǎn):要不斷輪詢 才能達(dá)到不斷監(jiān)聽源哩;

mBluetoothGatt.readCharacteristic(characteristic);

如果成功,將返回BluetoothGattCallback回調(diào) 進(jìn)入其如下方法

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

使用如下方法鸦做,傳入特征值励烦,true

mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
如果成功,將返回BluetoothGattCallback回調(diào) 進(jìn)入其如下方法

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }

具體選擇哪種方法就要看具體需求了泼诱。

3.還有一個(gè)很重要的方法坛掠,demo沒有給出例子的:
mBluetoothGatt.writeCharacteristic(characteristic);
這是向藍(lán)牙設(shè)備寫入數(shù)據(jù),幾乎都會(huì)用到的。
如果成功屉栓,將返回BluetoothGattCallback回調(diào) 進(jìn)入其如下方法

    @Override
    public void onCharacteristicWrite(BluetoothGatt gatt,
                                      BluetoothGattCharacteristic characteristic,
                                      int status) {
 
    }
  1. disconnect()舷蒲;調(diào)用BluetoothGatt.disconnect()斷開藍(lán)牙連接;
    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            Log.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }

5.close();關(guān)閉藍(lán)牙

    /**
     * After using a given BLE device, the app must call this method to ensure resources are
     * released properly.
     */
    public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

最后友多,關(guān)于Android藍(lán)牙BLE的使用在此結(jié)束阿纤,我從下載官方demo到一步一步地去理解具體的調(diào)用,最后已經(jīng)算是走通了整個(gè)藍(lán)牙開發(fā)流程夷陋,如果想再深入點(diǎn)的話就要考慮具體的底層實(shí)現(xiàn),和真實(shí)項(xiàng)目中怎么去更好地封裝胰锌!

掃一掃關(guān)注我的微信公眾號:程序猿在廣東

my二維碼.jpg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末骗绕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子资昧,更是在濱河造成了極大的恐慌酬土,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件格带,死亡現(xiàn)場離奇詭異撤缴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叽唱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門屈呕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棺亭,你說我怎么就攤上這事虎眨。” “怎么了镶摘?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵嗽桩,是天一觀的道長。 經(jīng)常有香客問我凄敢,道長碌冶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任涝缝,我火速辦了婚禮扑庞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拒逮。我一直安慰自己嫩挤,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布消恍。 她就那樣靜靜地躺著岂昭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上约啊,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天邑遏,我揣著相機(jī)與錄音,去河邊找鬼恰矩。 笑死记盒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的外傅。 我是一名探鬼主播纪吮,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼萎胰!你這毒婦竟也來了碾盟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤技竟,失蹤者是張志新(化名)和其女友劉穎冰肴,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體榔组,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熙尉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了搓扯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片检痰。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锨推,靈堂內(nèi)的尸體忽然破棺而出攀细,到底是詐尸還是另有隱情,我是刑警寧澤爱态,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布谭贪,位于F島的核電站,受9級特大地震影響锦担,放射性物質(zhì)發(fā)生泄漏俭识。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一洞渔、第九天 我趴在偏房一處隱蔽的房頂上張望套媚。 院中可真熱鬧,春花似錦磁椒、人聲如沸堤瘤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽本辐。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間慎皱,已是汗流浹背老虫。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留茫多,地道東北人祈匙。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像天揖,于是被迫代替她去往敵國和親夺欲。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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