神奇的藍(lán)牙之即時(shí)通訊

時(shí)間好快,轉(zhuǎn)眼又一個(gè)月了,每天工作很忙,也沒(méi)有什么時(shí)間學(xué)習(xí),下一篇打算研究一下 framwork 的一些東西。

藍(lán)牙

Android 平臺(tái)包含藍(lán)牙網(wǎng)絡(luò)堆棧支持,憑借此項(xiàng)支持,設(shè)備能以無(wú)線方式與其他藍(lán)牙設(shè)備交換數(shù)據(jù)。應(yīng)用框架提供了通過(guò) Android Bluetooth API 訪問(wèn)藍(lán)牙功能的途徑。 這些 API 允許應(yīng)用以無(wú)線方式連接到其他藍(lán)牙設(shè)備,從而實(shí)現(xiàn)點(diǎn)到點(diǎn)和多點(diǎn)無(wú)線功能。

傳統(tǒng)藍(lán)牙

使用藍(lán)牙進(jìn)行通信的四項(xiàng)主要任務(wù):

  • 設(shè)置藍(lán)牙
  • 查找局部區(qū)域內(nèi)的配對(duì)設(shè)備或可用設(shè)備
  • 連接設(shè)備
  • 在設(shè)備之間傳輸數(shù)據(jù)

1、聲明權(quán)限

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

2、設(shè)置藍(lán)牙

  • a、獲取 BluetoothAdapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Device does not support Bluetooth
}
  • b、啟用藍(lán)牙
if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

您的應(yīng)用還可以選擇偵聽(tīng) ACTION_STATE_CHANGED 廣播 Intent,每當(dāng)藍(lán)牙狀態(tài)發(fā)生變化時(shí),系統(tǒng)都會(huì)廣播此 Intent。 此廣播包含額外字段

  • EXTRA_STATE : 新的藍(lán)牙狀態(tài)
  • EXTRA_PREVIOUS_STATE : 舊的藍(lán)牙狀態(tài)

這些額外字段可能的值包括 :

  • STATE_TURNING_ON
  • STATE_ON
  • STATE_TURNING_OFF
  • STATE_OFF

偵聽(tīng)此廣播適用于檢測(cè)在您的應(yīng)用運(yùn)行期間對(duì)藍(lán)牙狀態(tài)所做的更改。

3、查找設(shè)備

設(shè)備發(fā)現(xiàn)是一個(gè)掃描過(guò)程,它會(huì)搜索局部區(qū)域內(nèi)已啟用藍(lán)牙功能的設(shè)備,然后請(qǐng)求一些關(guān)于各臺(tái)設(shè)備的信息。

但局部區(qū)域內(nèi)的藍(lán)牙設(shè)備僅在其當(dāng)前已啟用可檢測(cè)性時(shí)才會(huì)響應(yīng)發(fā)現(xiàn)請(qǐng)求。

利用此信息,執(zhí)行發(fā)現(xiàn)的設(shè)備可以選擇發(fā)起到被發(fā)現(xiàn)設(shè)備的連接。

請(qǐng)記住,被配對(duì)與被連接之間存在差別。被配對(duì)意味著兩臺(tái)設(shè)備知曉彼此的存在,具有可用于身份驗(yàn)證的共享鏈路密鑰,并且能夠與彼此建立加密連接。 被連接意味著設(shè)備當(dāng)前共享一個(gè) RFCOMM 通道,并且能夠向彼此傳輸數(shù)據(jù)。 當(dāng)前的 Android Bluetooth API 要求對(duì)設(shè)備進(jìn)行配對(duì),然后才能建立 RFCOMM 連接。

  • a、查詢配對(duì)的設(shè)備
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}
  • b、發(fā)現(xiàn)設(shè)備

要開(kāi)始發(fā)現(xiàn)設(shè)備,只需調(diào)用 startDiscovery()。該進(jìn)程為異步進(jìn)程,并且該方法會(huì)立即返回一個(gè)布爾值,指示是否已成功啟動(dòng)發(fā)現(xiàn)操作。

針對(duì) ACTION_FOUND Intent 注冊(cè)一個(gè) BroadcastReceiver,以便接收每臺(tái)發(fā)現(xiàn)的設(shè)備的相關(guān)信息。 針對(duì)每臺(tái)設(shè)備,系統(tǒng)將會(huì)廣播 ACTION_FOUND Intent。此 Intent 將攜帶額外字段

  • EXTRA_DEVICE // 包含 BluetoothDevice
  • EXTRA_CLASS // 包含 BluetoothClass
// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy
  • c、啟用可檢測(cè)性

如果您希望將本地設(shè)備設(shè)為可被其他設(shè)備檢測(cè)到,請(qǐng)使用

ACTION_REQUEST_DISCOVERABLE

操作 Intent ,您可以通過(guò)添加 :

EXTRA_DISCOVERABLE_DURATION 

Intent Extra 來(lái)定義不同的持續(xù)時(shí)間。 應(yīng)用可以設(shè)置的最大持續(xù)時(shí)間為 3600 秒,值為 0 則表示設(shè)備始終可檢測(cè)到。 任何小于 0 或大于 3600 的值都會(huì)自動(dòng)設(shè)為 120 秒。 例如,以下片段會(huì)將持續(xù)時(shí)間設(shè)為 300 秒:

Intent discoverableIntent = new
Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);

如果您希望在可檢測(cè)到模式發(fā)生變化時(shí)收到通知,您可以針對(duì)

ACTION_SCAN_MODE_CHANGED Intent 

注冊(cè) BroadcastReceiver。 它將包含額外字段

  • EXTRA_SCAN_MODE
  • EXTRA_PREVIOUS_SCAN_MODE

二者分別告知您新的和舊的掃描模式。 每個(gè)字段可能的值包括:

  • SCAN_MODE_CONNECTABLE_DISCOVERABLE :可檢測(cè)到模式
  • SCAN_MODE_CONNECTABLE :未處于可檢測(cè)到模式但仍能接收連接
  • SCAN_MODE_NONE :未處于可檢測(cè)到模式并且無(wú)法接收連接

4、連接設(shè)備

要在兩臺(tái)設(shè)備上的應(yīng)用之間創(chuàng)建連接,必須同時(shí)實(shí)現(xiàn)服務(wù)器端和客戶端機(jī)制,因?yàn)槠渲幸慌_(tái)設(shè)備必須開(kāi)放服務(wù)器套接字,而另一臺(tái)設(shè)備必須發(fā)起連接(使用服務(wù)器設(shè)備的 MAC 地址發(fā)起連接)。 當(dāng)服務(wù)器和客戶端在同一 RFCOMM 通道上分別擁有已連接的 BluetoothSocket 時(shí),二者將被視為彼此連接。

藍(lán)牙套接字接口(與 TCP Socket 相似)。這是允許應(yīng)用通過(guò) InputStream 和 OutputStream 與其他藍(lán)牙設(shè)備交換數(shù)據(jù)的連接點(diǎn)。

  • a、連接為服務(wù)器

以下是設(shè)置服務(wù)器套接字并接受連接的基本過(guò)程:

1、通過(guò)調(diào)用 listenUsingRfcommWithServiceRecord(String, UUID) 獲取 BluetoothServerSocket。

2、通過(guò)調(diào)用 accept() 開(kāi)始偵聽(tīng)連接請(qǐng)求。

3、除非您想要接受更多連接,否則請(qǐng)調(diào)用 close()。

這將釋放服務(wù)器套接字及其所有資源,但不會(huì)關(guān)閉 accept() 所返回的已連接的 BluetoothSocket。

accept() 調(diào)用不應(yīng)在主 Activity UI 線程中執(zhí)行,因?yàn)樗亲枞{(diào)用,并會(huì)阻止與應(yīng)用的任何其他交互。 在您的應(yīng)用所管理的新線程中使用 BluetoothServerSocket 或 BluetoothSocket 完成所有工作。

  • b、連接為客戶端

以下是基本過(guò)程:

1、使用 BluetoothDevice,通過(guò)調(diào)用 createRfcommSocketToServiceRecord(UUID) 獲取 BluetoothSocket。

2、通過(guò)調(diào)用 connect() 發(fā)起連接。

系統(tǒng)將會(huì)在遠(yuǎn)程設(shè)備上執(zhí)行 SDP 查找,以便匹配 UUID。 如果查找成功并且遠(yuǎn)程設(shè)備接受了該連接,它將共享 RFCOMM 通道以便在連接期間使用,并且 connect() 將會(huì)返回。 此方法為阻塞調(diào)用。

  • c、管理連接

在成功連接兩臺(tái)(或更多臺(tái))設(shè)備后,每臺(tái)設(shè)備都會(huì)有一個(gè)已連接的 BluetoothSocket。 這一點(diǎn)非常有趣,因?yàn)檫@表示您可以在設(shè)備之間共享數(shù)據(jù)。 利用 BluetoothSocket,傳輸任意數(shù)據(jù)的一般過(guò)程非常簡(jiǎn)單:

獲取 InputStream 和 OutputStream,二者分別通過(guò)套接字以及 getInputStream() 和 getOutputStream() 來(lái)處理數(shù)據(jù)傳輸。

使用 read(byte[]) 和 write(byte[]) 讀取數(shù)據(jù)并寫入到流式傳輸。

就這么簡(jiǎn)單。

5、使用配置文件

從 Android 3.0 開(kāi)始,Bluetooth API 便支持使用藍(lán)牙配置文件。 藍(lán)牙配置文件是適用于設(shè)備間藍(lán)牙通信的無(wú)線接口規(guī)范。

對(duì)于連接到無(wú)線耳機(jī)的手機(jī),兩臺(tái)設(shè)備都必須支持 “免提配置文件”。

Android Bluetooth API 提供了以下藍(lán)牙配置文件的實(shí)現(xiàn):

  • 耳機(jī)

耳機(jī)配置文件提供了藍(lán)牙耳機(jī)支持,以便與手機(jī)配合使用。

  • A2DP

高級(jí)音頻分發(fā)配置文件 (A2DP) 定義了高質(zhì)量音頻如何通過(guò)藍(lán)牙連接和流式傳輸,從一個(gè)設(shè)備傳輸?shù)搅硪粋€(gè)設(shè)備。 Android 提供了 BluetoothA2dp 類,它是用于通過(guò) IPC 來(lái)控制藍(lán)牙 A2DP 服務(wù)的代理。

  • 健康設(shè)備

Android 4.0(API 級(jí)別 14)引入了對(duì)藍(lán)牙健康設(shè)備配置文件 (HDP) 的支持。 這允許您創(chuàng)建應(yīng)用,使用藍(lán)牙與支持藍(lán)牙功能的健康設(shè)備進(jìn)行通信,例如心率監(jiān)測(cè)儀、血糖儀、溫度計(jì)、臺(tái)秤等等。

以下是使用配置文件的基本步驟:

1、獲取默認(rèn)適配器(請(qǐng)參閱設(shè)置藍(lán)牙)。

2、使用 getProfileProxy() 建立到配置文件所關(guān)聯(lián)的配置文件代理對(duì)象的連接。在以下示例中,配置文件代理對(duì)象是一個(gè) BluetoothHeadset 的實(shí)例。

3、設(shè)置 BluetoothProfile.ServiceListener。此偵聽(tīng)程序會(huì)在 BluetoothProfile IPC 客戶端連接到服務(wù)或斷開(kāi)服務(wù)連接時(shí)向其發(fā)送通知。

4、在 onServiceConnected() 中,獲取配置文件代理對(duì)象的句柄。

5、獲得配置文件代理對(duì)象后,可以立即將其用于監(jiān)視連接狀態(tài)和執(zhí)行其他與該配置文件相關(guān)的操作。

以下代碼片段顯示了如何連接到 BluetoothHeadset 代理對(duì)象,以便能夠控制耳機(jī)配置文件:

BluetoothHeadset mBluetoothHeadset;

// Get the default adapter
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

// Establish connection to the proxy.
mBluetoothAdapter.getProfileProxy(context, mProfileListener, BluetoothProfile.HEADSET);

private BluetoothProfile.ServiceListener mProfileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            mBluetoothHeadset = null;
        }
    }
};

// ... call functions on mBluetoothHeadset

// Close proxy connection after use.
mBluetoothAdapter.closeProfileProxy(mBluetoothHeadset);

供應(yīng)商特定的 AT 命令

從 Android 3.0 開(kāi)始,應(yīng)用可以注冊(cè)接收耳機(jī)所發(fā)送的預(yù)定義的供應(yīng)商特定 AT 命令的系統(tǒng)廣播(例如 Plantronics +XEVENT 命令)。

例如,應(yīng)用可以接收指示所連接設(shè)備的電池電量的廣播,并根據(jù)需要通知用戶或采取其他操作。 為

ACTION_VENDOR_SPECIFIC_HEADSET_EVENT intent

創(chuàng)建廣播接收器,以處理耳機(jī)的供應(yīng)商特定 AT 命令。

下面是獲取藍(lán)牙耳機(jī)電池電量:

 /**
     * Handle {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
     * @param intent must be {@link BluetoothHeadset#ACTION_VENDOR_SPECIFIC_HEADSET_EVENT} intent
     */
    @VisibleForTesting
    void onVendorSpecificHeadsetEvent(Intent intent) {
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        if (device == null) {
            Log.e(TAG, "onVendorSpecificHeadsetEvent() remote device is null");
            return;
        }
        String cmd =
                intent.getStringExtra(BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD);
        if (cmd == null) {
            Log.e(TAG, "onVendorSpecificHeadsetEvent() command is null");
            return;
        }
        int cmdType = intent.getIntExtra(
                BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_CMD_TYPE, -1);
        // Only process set command
        if (cmdType != BluetoothHeadset.AT_CMD_TYPE_SET) {
            debugLog("onVendorSpecificHeadsetEvent() only SET command is processed");
            return;
        }
        Object[] args = (Object[]) intent.getExtras().get(
                BluetoothHeadset.EXTRA_VENDOR_SPECIFIC_HEADSET_EVENT_ARGS);
        if (args == null) {
            Log.e(TAG, "onVendorSpecificHeadsetEvent() arguments are null");
            return;
        }
        int batteryPercent = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
        switch (cmd) {
            case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_XEVENT:
                batteryPercent = getBatteryLevelFromXEventVsc(args);
                break;
            case BluetoothHeadset.VENDOR_SPECIFIC_HEADSET_EVENT_IPHONEACCEV:
                batteryPercent = getBatteryLevelFromAppleBatteryVsc(args);
                break;
        }
        if (batteryPercent != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
            updateBatteryLevel(device, batteryPercent);
            infoLog("Updated device " + device + " battery level to "
                    + String.valueOf(batteryPercent) + "%");
        }
    }

健康設(shè)備配置文件

  • 源設(shè)備:在 HDP 中定義的角色。源設(shè)備是將醫(yī)療數(shù)據(jù)傳輸?shù)?Android 手機(jī)或平板電腦等智能設(shè)備的健康設(shè)備(體重秤、血糖儀、溫度計(jì)等)。

  • 匯集設(shè)備:在 HDP 中定義的角色。在 HDP 中,匯集設(shè)備是接收醫(yī)療數(shù)據(jù)的智能設(shè)備。 在 Android HDP 應(yīng)用中,匯集設(shè)備表示為 BluetoothHealthAppConfiguration 對(duì)象。

  • 注冊(cè):指的是注冊(cè)特定健康設(shè)備的匯集設(shè)備。

  • 連接:指的是開(kāi)放健康設(shè)備與 Android 手機(jī)或平板電腦等智能設(shè)備之間的通道。

創(chuàng)建 HDP 應(yīng)用

以下是創(chuàng)建 Android HDP 應(yīng)用所涉及的基本步驟:

1、獲取 BluetoothHealth 代理對(duì)象的引用。

2、與常規(guī)耳機(jī)和 A2DP 配置文件設(shè)備相似,您必須使用 BluetoothProfile.ServiceListener 和 HEALTH 配置文件類型來(lái)調(diào)用 getProfileProxy(),以便與配置文件代理對(duì)象建立連接。

3、創(chuàng)建 BluetoothHealthCallback 并注冊(cè)充當(dāng)健康匯集設(shè)備的應(yīng)用配置 (BluetoothHealthAppConfiguration)。

4、建立到健康設(shè)備的連接。一些設(shè)備將會(huì)發(fā)起該連接。 對(duì)于這類設(shè)備,無(wú)需執(zhí)行該步驟。
成功連接到健康設(shè)備后,使用文件描述符對(duì)健康設(shè)備執(zhí)行讀/寫操作。

5、接收的數(shù)據(jù)需要使用實(shí)現(xiàn)了 IEEE 11073-xxxxx 規(guī)范的健康管理器進(jìn)行解釋。

完成后,關(guān)閉健康通道并取消注冊(cè)該應(yīng)用。該通道在長(zhǎng)期閑置時(shí)也會(huì)關(guān)閉。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 這兩天在讀大江健三郎先生的《康復(fù)的家庭》和《寬松的紐帶》。這是兩本散文集和《在自己樹(shù)下》同屬一個(gè)系列,是記錄以智障...
    李清淺閱讀 1,104評(píng)論 0 6
  • 5月26日 2018/05/26 巴塞羅那馬而托雷利,大部多云,氣溫24/15°c。 昨晚住于Ciutat M...
    H老姜閱讀 456評(píng)論 0 0
  • 我允許,我接納,我臣服,我………… 我允許任何事情的發(fā)生。 我允許,事情是如此的開(kāi)始, 如此的發(fā)展,如此的結(jié)局。 ...
    新麗0101閱讀 680評(píng)論 0 0
  • 夏的蚊香: 人生如一盤“蚊香”,不同的起點(diǎn),相同的終點(diǎn)! 有人越走越遠(yuǎn),有人回到原點(diǎn)! 一直轉(zhuǎn)一直轉(zhuǎn),像漩渦,像輪...
    學(xué)習(xí)香道者閱讀 1,032評(píng)論 0 0

友情鏈接更多精彩內(nèi)容