Android BLE

Key Terms And Concepts

关键术语和概念

Here is a summary of key BLE terms and concepts:
以下是关于BLE的关键术语和概念的摘要

  • Generic Attribute Profile (GATT)—The GATT profile is a general specification for sending and receiving short pieces of data known as "attributes" over a BLE link. All current Low Energy application profiles are based on GATT.
    通用属性配置文件(GATT)--通用属性配置文件是一个通过BLE链接发送或接受短小片段数据或被称为“属性”的通用规范。当前所有的低功耗应用配置文件都基于GATT。

    • The Bluetooth SIG defines many profiles for Low Energy devices. A profile is a specification for how a device works in a particular application. Note that a device can implement more than one profile. For example, a device could contain a heart rate monitor and a battery level detector.
    • 蓝牙技术联盟为低功耗设备定义了很多配属文件。配置文件是一个在特定应用中设备如何工作的规范。请注意,一个设备可以实现多个配置文件。例如:一个设备可以包含一个心跳检测器和一个电量检测器
  • Attribute Protocol (ATT)—GATT is built on top of the Attribute Protocol (ATT). This is also referred to as GATT/ATT. ATT is optimized to run on BLE devices. To this end, it uses as few bytes as possible. Each attribute is uniquely identified by a Universally Unique Identifier (UUID), which is a standardized 128-bit format for a string ID used to uniquely identify information. The attributes transported by ATT are formatted as characteristics and services.
    属性协议(ATT)——GATT建立在属性协议(ATT)之上。这也被称为GATT/ATT。ATT经过优化,可在BLE设备上运行。为此,它使用尽可能少的字节。每个属性由通用唯一标识符(UUID)唯一标识,该标识符是用于唯一标识信息的标准化128-bit格式的字符串ID。由ATT传输的属性被格式化为特征和服务

  • Characteristic—A characteristic contains a single value and 0-n descriptors that describe the characteristic's value. A characteristic can be thought of as a type, analogous to a class.
    特征——一个特征包含一个值和0至多个描述特征的描述符。一个特征可以被认为是一种类型,类似于一个类。

  • Descriptor—Descriptors are defined attributes that describe a characteristic value. For example, a descriptor might specify a human-readable description, an acceptable range for a characteristic's value, or a unit of measure that is specific to a characteristic's value.
    描述符——描述符是描述特征值的定义属性。例如:描述符也许可以指定一个人类可读的描述,特征值的可接受范围,或特征值特有的单位

  • Service—A service is a collection of characteristics. For example, you could have a service called "Heart Rate Monitor" that includes characteristics such as "heart rate measurement." You can find a list of existing GATT-based profiles and services on bluetooth.org.
    服务——服务是一个特征的集合。例如:你可以使用一个叫做“心跳检测器”的服务,其中包括“心跳测量”等特征。你可以在bluetooth.org上找到一个已经存在的基于GATT的配置文件和服务的列表

Roles and Responsibilities

角色和职责

Here are the roles and responsibilities that apply when an Android device interacts with a BLE device:
这里是Android设备与BLE设备交互时适用的角色和职责:

  • Central vs. peripheral. This applies to the BLE connection itself. The device in the central role scans, looking for advertisement, and the device in the peripheral role makes the advertisement.
    中央和外设。这适用于BLE连接本身。中央设备角色扫描,寻找广告,同时外角色设备创建广告。

  • GATT server vs. GATT client. This determines how two devices talk to each other once they've established the connection.
    GATT服务器和GATT客户端。这决定了两个设备建立连接之后如何通信。

To understand the distinction, imagine that you have an Android phone and an activity tracker that is a BLE device. The phone supports the central role; the activity tracker supports the peripheral role (to establish a BLE connection you need one of each—two things that only support peripheral couldn't talk to each other, nor could two things that only support central).
为了理解这个特性,假设你拥有一个Android手机和一个BLE活动追踪器设备。手机担任中央角色;活动追踪器担任外设角色(为了建立BLE连接,你需要一对的这样的设备。只支持外设角色的两个设备不能互相通信,同样,仅支持中央角色的两个设备也不能互相通信)

Once the phone and the activity tracker have established a connection, they start transferring GATT metadata to one another. Depending on the kind of data they transfer, one or the other might act as the server. For example, if the activity tracker wants to report sensor data to the phone, it might make sense for the activity tracker to act as the server. If the activity tracker wants to receive updates from the phone, then it might make sense for the phone to act as the server.
手机和活动追踪器一旦建立了连接,他们就开始互相传输GATT元数据。依赖于他们传输的数据,其中的一个开始充当服务器。例如:如果活动跟踪器希望将传感器数据报告给手机,那么活动跟踪器可能会充当服务器。如何活动追踪器想要从手机接收更新,那么手机可能会充当服务器。

In the example used in this document, the Android app (running on an Android device) is the GATT client. The app gets data from the GATT server, which is a BLE heart rate monitor that supports the Heart Rate Profile. But you could alternatively design your Android app to play the GATT server role. See BluetoothGattServer for more information.
这个文档中使用的例子中,Android APP(运行在Android设备上)是GATT客户端。App从一个GATT服务器中获取数据,GATT服务器是一个提供心跳配置文件的BLE心跳检测器。但是你也可以设计你的安卓App充当GATT服务器的角色。有关详细信息,请查看BluetoothGattServer。

BLE 权限


In order to use Bluetooth features in your application, you must declare the Bluetooth permission BLUETOOTH. You need this permission to perform any Bluetooth communication, such as requesting a connection, accepting a connection, and transferring data.
为了在你的应用中使用蓝牙特性,你必须要申明蓝牙权限 BLUETOOTH 。你要此权限才能执行任何蓝牙通信,如:请求一个连接,接收一个连接,和传输数据。

If you want your app to initiate device discovery or manipulate Bluetooth settings, you must also declare the BLUETOOTH_ADMIN permission. Note: If you use the BLUETOOTH_ADMIN permission, then you must also have the BLUETOOTH permission.
如果你想要你的app启动设备发现或操纵蓝牙设置,你必须也要申明 BLUETOOTH_ADMIN 权限。注意:如果你用了 BLUETOOTH_ADMIN 权限,则还必须有 BLUETOOTH 权限。

Declare the Bluetooth permission(s) in your application manifest file. For example:
声明蓝牙权限在你的应用manifest文件,例如:

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

If you want to declare that your app is available to BLE-capable devices only, include the following in your app's manifest:
如果你想要申明你的app仅支持有BLE功能的设备,在你的app的manifest中填上如下内容:

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
However, if you want to make your app available to devices that don't support BLE, you should still include this element in your app's manifest, but set required="false". Then at run-time you can determine BLE availability by using PackageManager.hasSystemFeature():
然后,如果你想要你的app能够被不支持BLE的设备获取,你依然需要包含这个要素在你的app的manifest文件中,但是,需要设置required为false。然后在你的代码运行时,你可以通过使用确认PackageManager.hasSystemFeature()方法来确认BLE是否可获取。

// Use this check to determine whether BLE is supported on the device. Then
// you can selectively disable BLE-related features.
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
    finish();
}

Note: LE Beacons are often associated with location. In order to use BluetoothLeScanner without a filter, you must request the user's permission by declaring either the ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission in your app's manifest file. Without these permissions, scans won't return any results.

注意:LE信标通常与位置相关联。
为了在没有过滤器的情况下使用BluetoothLeScanner,您必须通过声明应用程序的清单文件中的ACCESS_COARSE_LOCATION或ACCESS_FINE_LOCATION权限来请求用户的权限。

Setting Up BLE

设置BLE
Before your application can communicate over BLE, you need to verify that BLE is supported on the device, and if so, ensure that it is enabled. Note that this check is only necessary if <uses-feature.../> is set to false.
在你应用可以通过BLE通信之前,你需要验证这个设备是否支持BLE,如果可以,确认BLE是使能的。需要注意的是只有当<uses-feature.../>设置为false这个是检查必要的

If BLE is not supported, then you should gracefully disable any BLE features. If BLE is supported, but disabled, then you can request that the user enable Bluetooth without leaving your application. This setup is accomplished in two steps, using the BluetoothAdapter.
如果BLE不被支持,则你需要优雅的除能所有的BLE特性。如果BLE被支持,但是被除能了,则你需要请求用户使能蓝牙而不离开的应用。该设置使用BluetoothAdapter在两个步骤中完成。

  1. Get the BluetoothAdapter
    获取BluetoothAdapter
    The BluetoothAdapter is required for any and all Bluetooth activity. The BluetoothAdapter represents the device's own Bluetooth adapter (the Bluetooth radio). There's one Bluetooth adapter for the entire system, and your application can interact with it using this object. The snippet below shows how to get the adapter. Note that this approach uses getSystemService() to return an instance of BluetoothManager, which is then used to get the adapter. Android 4.3 (API Level 18) introduces BluetoothManager:
    所有的蓝牙Activity中都需要蓝牙适配器。蓝牙适配器表示了这个设备自带的蓝牙适配器(蓝牙无线电)。整个系统有一个蓝牙适配器,你的应用可以通过这个对象和它交互。下面的代码片段展示了如何获取这个适配器。需要注意的是这个方法使用getSystemService()去获取BluetoothManager的一个实例,然后用于获取适配器。Android 4.3(API Level 18)介绍了BluetoothManager。
    private BluetoothAdapter mBluetoothAdapter;
    ...
    // Initializes Bluetooth adapter.
    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    
  2. Enable Bluetooth
    使能蓝牙
    Next, you need to ensure that Bluetooth is enabled. Call isEnabled() to check whether Bluetooth is currently enabled. If this method returns false, then Bluetooth is disabled. The following snippet checks whether Bluetooth is enabled. If it isn't, the snippet displays an error prompting the user to go to Settings to enable Bluetooth:
    接下来,你需要确认蓝牙是使能的。调用isEnable()去检查是否蓝牙当前是否使能。如果这个方法返回false,则蓝牙是除能的。接下来的代码片段检查是否蓝牙是是能的。如果没有使能,代码片段会显示一个用户去设置开启蓝牙的错误提示。
    // Ensures Bluetooth is available on the device and it is enabled. If not,
    // displays a dialog requesting user permission to enable Bluetooth.
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    

Note: The REQUEST_ENABLE_BT constant passed to startActivityForResult(android.content.Intent, int) is a locally-defined integer (which must be greater than 0) that the system passes back to you in your onActivityResult(int, int, android.content.Intent) implementation as the requestCode parameter.

注意:传送给startActivityForResult()的常量REQUEST_ENABLE_BT是一个本地定义的整数(必须大于0),这个整数系统会实现为requestCode参数,在你的onActivityResult()方法中,回传给你。

Finding BLE Devices

寻找BLE设备

To find BLE devices, you use the startLeScan() method. This method takes a BluetoothAdapter.LeScanCallback as a parameter. You must implement this callback, because that is how scan results are returned. Because scanning is battery-intensive, you should observe the following guidelines:
要寻找BLE设备,你可以使用startLeScan()方法。这个方法带有一个BluetoothAdapter.LeScanCallback作为参数。你必须要实现这个回调,因为这是如何返回扫描结果的(因为扫描结果是通过这个返回的)。因为扫描是电池密集型的,你需要遵循以下的准则:

  • As soon as you find the desired device, stop scanning.
    只要你一找到了想要的设备,就停止扫描
  • Never scan on a loop, and set a time limit on your scan. A device that was previously available may have moved out of range, and continuing to scan drains the battery.
    切勿在循环里扫描,且要设置一个扫描时间限制。 一个之前可以获取的设备也许已经移出了范围,持续扫描消耗电池。

The following snippet shows how to start and stop a scan:
下面的代码片段展示了如何开始和停止扫描:

/**
 * Activity for scanning and displaying available BLE devices.
 */
public class DeviceScanActivity extends ListActivity {

    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;
    ...
    private void scanLeDevice(final boolean enable) {
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...
    }
...
}

If you want to scan for only specific types of peripherals, you can instead call startLeScan(UUID[], BluetoothAdapter.LeScanCallback), providing an array of UUID objects that specify the GATT services your app supports.
如果你想扫描特定类型的外设,你可以替换调用startLeScan(UUID[], BluetoothAdapter.LeScanCallback),提供一个特定的你的app支持的GATT服务UUID设备数组。

Here is an implementation of the BluetoothAdapter.LeScanCallback, which is the interface used to deliver BLE scan results:
这里有一个BluetoothAdapter.LeScanCallback的实现,它是一个接口,用来传输BLE扫描结果:

private LeDeviceListAdapter mLeDeviceListAdapter;
...
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback =
        new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, int rssi,
            byte[] scanRecord) {
        runOnUiThread(new Runnable() {
           @Override
           public void run() {
               mLeDeviceListAdapter.addDevice(device);
               mLeDeviceListAdapter.notifyDataSetChanged();
           }
       });
   }
};

Note: You can only scan for Bluetooth LE devices or scan for Classic Bluetooth devices, as described in Bluetooth. You cannot scan for both Bluetooth LE and classic devices at the same time.

注意:你可以扫描低功耗蓝牙设备或经典蓝牙设备,如蓝牙所述。你不能同时扫描低功耗蓝牙设备和经典蓝牙设备。

Connecting to a GATT Server

连接GATT服务器

The first step in interacting with a BLE device is connecting to it— more specifically, connecting to the GATT server on the device. To connect to a GATT server on a BLE device, you use the connectGatt() method. This method takes three parameters: a Context object, autoConnect (boolean indicating whether to automatically connect to the BLE device as soon as it becomes available), and a reference to a BluetoothGattCallback:
与BLE设备的交互的第一步是连接到它-更具体的说,连接在这个BLE设备上的GATT服务器。为了连接上在这个BLE设备上的GATT服务器,你可以使用connectGatt()方法。这个方法有三个参数:一个上下文对象,是否自动连接(一个布尔值,表明只要这个BLE设备是可以获取的,是否自动的连接上它),一个指向BluetoothGattCallback的引用。

mBluetoothGatt = device.connectGatt(this, false, mGattCallback);

This connects to the GATT server hosted by the BLE device, and returns a BluetoothGatt instance, which you can then use to conduct GATT client operations. The caller (the Android app) is the GATT client. The BluetoothGattCallback is used to deliver results to the client, such as connection status, as well as any further GATT client operations.
这会连接到由BLE设备管理的GATT服务器,并返回一个BluetoothGatt实例,然后您可以使用它来进行GATT客户端操作。调用者(Android app)是GATT客户端。BluetoothGattCallback用来传送结果给客户端,例如连接状态,以及任何进一步的GATT客户端操作。

In this example, the BLE app provides an activity (DeviceControlActivity) to connect, display data, and display GATT services and characteristics supported by the device. Based on user input, this activity communicates with a Service called BluetoothLeService, which interacts with the BLE device via the Android BLE API:
这个例子中,这个BLE应用提供一个活动(DeviceControlActivity)用于连接设备,显示由设备提供的数据,GATT服务和特征。基于用户输入,这个活动与一个名为BluetoothLeService的服务通信,该服务通过Android BLE API与BLE设备进行交互:

// A service that interacts with the BLE device via the Android BLE API.
public class BluetoothLeService extends Service {
    private final static String TAG = BluetoothLeService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;
    private BluetoothGatt mBluetoothGatt;
    private int mConnectionState = STATE_DISCONNECTED;

    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTING = 1;
    private static final int STATE_CONNECTED = 2;

    public final static String ACTION_GATT_CONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
    public final static String ACTION_GATT_DISCONNECTED =
            "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
    public final static String ACTION_GATT_SERVICES_DISCOVERED =
            "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
    public final static String ACTION_DATA_AVAILABLE =
            "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
    public final static String EXTRA_DATA =
            "com.example.bluetooth.le.EXTRA_DATA";

    public final static UUID UUID_HEART_RATE_MEASUREMENT =
            UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

    // Various callback methods defined by the BLE API.
    private final BluetoothGattCallback mGattCallback =
            new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status,
                int newState) {
            String intentAction;
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                intentAction = ACTION_GATT_CONNECTED;
                mConnectionState = STATE_CONNECTED;
                broadcastUpdate(intentAction);
                Log.i(TAG, "Connected to GATT server.");
                Log.i(TAG, "Attempting to start service discovery:" +
                        mBluetoothGatt.discoverServices());

            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                intentAction = ACTION_GATT_DISCONNECTED;
                mConnectionState = STATE_DISCONNECTED;
                Log.i(TAG, "Disconnected from GATT server.");
                broadcastUpdate(intentAction);
            }
        }

        @Override
        // New services discovered
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

        @Override
        // Result of a characteristic read operation
        public void onCharacteristicRead(BluetoothGatt gatt,
                BluetoothGattCharacteristic characteristic,
                int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
            }
        }
     ...
    };
...
}

When a particular callback is triggered, it calls the appropriate broadcastUpdate() helper method and passes it an action. Note that the data parsing in this section is performed in accordance with the Bluetooth Heart Rate Measurement profile specifications:
当特定的回调被触发时,它调用相应的broadcastUpdate()辅助方法并传递一个动作。请注意,本节中的数据解析是根据蓝牙心率测量配置文件规范执行的:

private void broadcastUpdate(final String action) {
    final Intent intent = new Intent(action);
    sendBroadcast(intent);
}

private void broadcastUpdate(final String action,
                             final BluetoothGattCharacteristic characteristic) {
    final Intent intent = new Intent(action);

    // This is special handling for the Heart Rate Measurement profile. Data
    // parsing is carried out as per profile specifications.
    if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
        int flag = characteristic.getProperties();
        int format = -1;
        if ((flag & 0x01) != 0) {
            format = BluetoothGattCharacteristic.FORMAT_UINT16;
            Log.d(TAG, "Heart rate format UINT16.");
        } else {
            format = BluetoothGattCharacteristic.FORMAT_UINT8;
            Log.d(TAG, "Heart rate format UINT8.");
        }
        final int heartRate = characteristic.getIntValue(format, 1);
        Log.d(TAG, String.format("Received heart rate: %d", heartRate));
        intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
    } else {
        // For all other profiles, writes the data formatted in HEX.
        final byte[] data = characteristic.getValue();
        if (data != null && data.length > 0) {
            final StringBuilder stringBuilder = new StringBuilder(data.length);
            for(byte byteChar : data)
                stringBuilder.append(String.format("%02X ", byteChar));
            intent.putExtra(EXTRA_DATA, new String(data) + "\n" +
                    stringBuilder.toString());
        }
    }
    sendBroadcast(intent);
}

Back in DeviceControlActivity, these events are handled by a BroadcastReceiver:
回到DeviceControlActivity,这些事件都被一个BroadcastReceiver接收处理:

// Handles various events fired by the Service.
// ACTION_GATT_CONNECTED: connected to a GATT server.
// ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
// ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
// ACTION_DATA_AVAILABLE: received data from the device. This can be a
// result of read or notification operations.
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));
        }
    }
};

Reading BLE Attributes

读取BLE属性

Once your Android app has connected to a GATT server and discovered services, it can read and write attributes, where supported. For example, this snippet iterates through the server's services and characteristics and displays them in the UI:
一旦你的Android应用连接到GATT服务器并发现了服务,如果GATT服务器支持,它就可以读取和写入属性了。例如:这一片段代码遍历了这个服务器的服务和特征,,并将其显示在UI中:

public class DeviceControlActivity extends Activity {
    ...
    // Demonstrates how to iterate through the supported GATT
    // Services/Characteristics.
    // In this sample, we populate the data structure that is bound to the
    // ExpandableListView on the UI.
    private void displayGattServices(List<BluetoothGattService> gattServices) {
        if (gattServices == null) return;
        String uuid = null;
        String unknownServiceString = getResources().
                getString(R.string.unknown_service);
        String unknownCharaString = getResources().
                getString(R.string.unknown_characteristic);
        ArrayList<HashMap<String, String>> gattServiceData =
                new ArrayList<HashMap<String, String>>();
        ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                = new ArrayList<ArrayList<HashMap<String, String>>>();
        mGattCharacteristics =
                new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

        // Loops through available GATT Services.
        for (BluetoothGattService gattService : gattServices) {
            HashMap<String, String> currentServiceData =
                    new HashMap<String, String>();
            uuid = gattService.getUuid().toString();
            currentServiceData.put(
                    LIST_NAME, SampleGattAttributes.
                            lookup(uuid, unknownServiceString));
            currentServiceData.put(LIST_UUID, uuid);
            gattServiceData.add(currentServiceData);

            ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                    new ArrayList<HashMap<String, String>>();
            List<BluetoothGattCharacteristic> gattCharacteristics =
                    gattService.getCharacteristics();
            ArrayList<BluetoothGattCharacteristic> charas =
                    new ArrayList<BluetoothGattCharacteristic>();
           // Loops through available Characteristics.
            for (BluetoothGattCharacteristic gattCharacteristic :
                    gattCharacteristics) {
                charas.add(gattCharacteristic);
                HashMap<String, String> currentCharaData =
                        new HashMap<String, String>();
                uuid = gattCharacteristic.getUuid().toString();
                currentCharaData.put(
                        LIST_NAME, SampleGattAttributes.lookup(uuid,
                                unknownCharaString));
                currentCharaData.put(LIST_UUID, uuid);
                gattCharacteristicGroupData.add(currentCharaData);
            }
            mGattCharacteristics.add(charas);
            gattCharacteristicData.add(gattCharacteristicGroupData);
         }
    ...
    }
...
}

Receiving GATT Notifications

接收GATT通知

It's common for BLE apps to ask to be notified when a particular characteristic changes on the device. This snippet shows how to set a notification for a characteristic, using the setCharacteristicNotification() method:
当设备上一个特定的特征发生改变时,BLE应用去要求被通知很普遍。这段代码展示了如何通过使用setCharacteristicNotification()方法,去为一个特征设置一个通知:

private BluetoothGatt mBluetoothGatt;
BluetoothGattCharacteristic characteristic;
boolean enabled;
...
mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
...
BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
        UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
mBluetoothGatt.writeDescriptor(descriptor);

Once notifications are enabled for a characteristic, an onCharacteristicChanged() callback is triggered if the characteristic changes on the remote device:
一旦一个特征被使能通知,如果远程设备上的这个特征发生了改变,一个onCharacteristicChanged()回调被触发。

@Override
// Characteristic notification
public void onCharacteristicChanged(BluetoothGatt gatt,
        BluetoothGattCharacteristic characteristic) {
    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}

Closing the Client App

关闭客户端app

Once your app has finished using a BLE device, it should call close() so the system can release resources appropriately:
一旦你的应用程序完成使用BLE设备,它应该调用close(),以便系统可以正确释放资源:

public void close() {
    if (mBluetoothGatt == null) {
        return;
    }
    mBluetoothGatt.close();
    mBluetoothGatt = null;
}

后记:本文翻译自google开发者网站。链接如下:
https://developer.android.google.cn/guide/topics/connectivity/bluetooth-le.html
欢迎转载,但请尊重作者工作,留下本文后记
作者:Jaesoon
邮箱:jayyuz@163.com
日期:2017-09-17

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,080评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,422评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,630评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,554评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,662评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,856评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,014评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,752评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,212评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,541评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,687评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,347评论 4 331
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,973评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,777评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,006评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,406评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,576评论 2 349

推荐阅读更多精彩内容