Android NDK开发详解连接性之 连接到 GATT 服务器
与 BLE 设备交互的第一步是连接到 BLE 设备。更具体地说,就是连接到设备上的 GATT 服务器。如需连接到 BLE 设备上的 GATT 服务器,请使用 connectGatt() 方法。此方法采用三个参数:Context 对象、autoConnect(布尔值,指示是否在 BLE 设备可用时立即自动连接到该设备)以及对 BluetoothGattCallback 的引用:
Kotlin
var bluetoothGatt: BluetoothGatt? = null
...
bluetoothGatt = device.connectGatt(this, false, gattCallback)
Java
bluetoothGatt = device.connectGatt(this, false, gattCallback);
该操作将连接到由 BLE 设备托管的 GATT 服务器,并返回 BluetoothGatt 实例,然后您可以使用该实例执行 GATT 客户端操作。调用方(Android 应用)是 GATT 客户端。BluetoothGattCallback 用于向客户端传递结果(例如连接状态)以及任何进一步的 GATT 客户端操作。
设置绑定服务
在以下示例中,BLE 应用提供了一个 activity (DeviceControlActivity),用于连接到蓝牙设备、显示设备数据以及显示设备支持的 GATT 服务和特性。此 activity 会根据用户输入与名为 BluetoothLeService 的 Service 通信,后者通过 BLE API 与 BLE 设备交互。通信是使用绑定服务执行的,该服务允许 activity 连接到 BluetoothLeService 并调用函数以连接到设备。BluetoothLeService 需要一个 Binder 实现,以便提供对 activity 服务的访问权限。
Kotlin
class BluetoothLeService : Service() {
private val binder = LocalBinder()
override fun onBind(intent: Intent): IBinder? {
return binder
}
inner class LocalBinder : Binder() {
fun getService() : BluetoothLeService {
return this@BluetoothLeService
}
}
}
Java
class BluetoothLeService extends Service {
private Binder binder = new LocalBinder();
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
class LocalBinder extends Binder {
public BluetoothLeService getService() {
return BluetoothLeService.this;
}
}
}
该 activity 可以使用 bindService() 启动服务,传入 Intent 来启动服务、ServiceConnection 实现(用于监听连接和断开连接事件)以及一个标志(用于指定其他连接选项)。
Kotlin
class DeviceControlActivity : AppCompatActivity() {
private var bluetoothService : BluetoothLeService? = null
// Code to manage Service lifecycle.
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(
componentName: ComponentName,
service: IBinder
) {
bluetoothService = (service as LocalBinder).getService()
bluetoothService?.let { bluetooth ->
// call functions on service to check connection and connect to devices
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
bluetoothService = null
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.gatt_services_characteristics)
val gattServiceIntent = Intent(this, BluetoothLeService::class.java)
bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE)
}
}
Java
class DeviceControlActivity extends AppCompatActivity {
private BluetoothLeService bluetoothService;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothService = ((LocalBinder) service).getService();
if (bluetoothService != null) {
// call functions on service to check connection and connect to devices
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bluetoothService = null;
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gatt_services_characteristics);
Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
bindService(gattServiceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
设置 BluetoothAdapter
将服务绑定到后,就需要访问 BluetoothAdapter。应检查该适配器是否在设备上可用。如需详细了解 BluetoothAdapter,请参阅设置蓝牙。以下示例将此设置代码封装在 initialize() 函数中,该函数会返回表示成功的 Boolean 值。
Kotlin
private const val TAG = "BluetoothLeService"
class BluetoothLeService : Service() {
private var bluetoothAdapter: BluetoothAdapter? = null
fun initialize(): Boolean {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (bluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.")
return false
}
return true
}
...
}
Java
class BluetoothLeService extends Service {
public static final String TAG = "BluetoothLeService";
private BluetoothAdapter bluetoothAdapter;
public boolean initialize() {
bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
return true;
}
...
}
activity 会在其 ServiceConnection 实现中调用此函数。 如何处理 initialize() 函数的 false 返回值取决于您的应用。您可以向用户显示错误消息,指明当前设备不支持蓝牙操作,或停用任何需要蓝牙才能正常运行的功能。在以下示例中,对 activity 调用了 finish(),用于将用户返回上一屏幕。
Kotlin
class DeviceControlActivity : AppCompatActivity() {
// Code to manage Service lifecycle.
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(
componentName: ComponentName,
service: IBinder
) {
bluetoothService = (service as LocalBinder).getService()
bluetoothService?.let { bluetooth ->
if (!bluetooth.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth")
finish()
}
// perform device connection
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
bluetoothService = null
}
}
...
}
Java
class DeviceControlsActivity extends AppCompatActivity {
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothService = ((LocalBinder) service).getService();
if (bluetoothService != null) {
if (!bluetoothService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// perform device connection
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bluetoothService = null;
}
};
...
}
连接到设备
初始化 BluetoothService 后,它就可以连接到 BLE 设备。activity 需要将设备地址发送给服务,以便发起连接。该服务将首先对 BluetoothAdapter 调用 getRemoteDevice() 以访问设备。如果适配器找不到具有该地址的设备,getRemoteDevice() 会抛出 IllegalArgumentException。
Kotlin
fun connect(address: String): Boolean {
bluetoothAdapter?.let { adapter ->
try {
val device = adapter.getRemoteDevice(address)
} catch (exception: IllegalArgumentException) {
Log.w(TAG, "Device not found with provided address.")
return false
}
// connect to the GATT server on the device
} ?: run {
Log.w(TAG, "BluetoothAdapter not initialized")
return false
}
}
Java
public boolean connect(final String address) {
if (bluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address.");
return false;
}
// connect to the GATT server on the device
}
服务初始化后,DeviceControlActivity 会调用此 connect() 函数。activity 需要传入 BLE 设备的地址。在以下示例中,设备地址会作为 intent extra 传递给 activity。
Kotlin
// Code to manage Service lifecycle.
private val serviceConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(
componentName: ComponentName,
service: IBinder
) {
bluetoothService = (service as LocalBinder).getService()
bluetoothService?.let { bluetooth ->
if (!bluetooth.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth")
finish()
}
// perform device connection
bluetooth.connect(deviceAddress)
}
}
override fun onServiceDisconnected(componentName: ComponentName) {
bluetoothService = null
}
}
Java
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bluetoothService = ((LocalBinder) service).getService();
if (bluetoothService != null) {
if (!bluetoothService.initialize()) {
Log.e(TAG, "Unable to initialize Bluetooth");
finish();
}
// perform device connection
bluetoothService.connect(deviceAddress);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
bluetoothService = null;
}
};
声明 GATT 回调
一旦 activity 告知服务要连接到哪个设备,并且服务已连接到该设备,服务就需要连接到 BLE 设备上的 GATT 服务器。此连接需要 BluetoothGattCallback 来接收关于连接状态、服务发现、特性读取和特性通知的通知。
本主题重点介绍连接状态通知。如需了解如何执行服务发现、特征读取和请求特征通知,请参阅传输 BLE 数据。
当与设备 GATT 服务器的连接发生更改时,系统会触发 onConnectionStateChanged() 函数。在以下示例中,回调是在 Service 类中定义的,因此在服务连接到该回调后,该回调就可以与 BluetoothDevice 搭配使用。
Kotlin
private val bluetoothGattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
}
}
}
Java
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
}
}
};
连接到 GATT 服务
声明 BluetoothGattCallback 后,该服务可以使用 connect() 函数中的 BluetoothDevice 对象连接到设备上的 GATT 服务。
其中使用了 connectGatt() 函数。这需要一个 Context 对象、一个 autoConnect 布尔值标志和 BluetoothGattCallback。在此示例中,应用会直接连接到 BLE 设备,因此系统会为 autoConnect 传递 false。
此外,还添加了 BluetoothGatt 属性。这样,服务就可在不再需要时关闭连接。
Kotlin
class BluetoothLeService : Service() {
...
private var bluetoothGatt: BluetoothGatt? = null
...
fun connect(address: String): Boolean {
bluetoothAdapter?.let { adapter ->
try {
val device = adapter.getRemoteDevice(address)
// connect to the GATT server on the device
bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback)
return true
} catch (exception: IllegalArgumentException) {
Log.w(TAG, "Device not found with provided address. Unable to connect.")
return false
}
} ?: run {
Log.w(TAG, "BluetoothAdapter not initialized")
return false
}
}
}
Java
class BluetoothService extends Service {
...
private BluetoothGatt bluetoothGatt;
...
public boolean connect(final String address) {
if (bluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
try {
final BluetoothDevice device = bluetoothAdapter.getRemoteDevice(address);
// connect to the GATT server on the device
bluetoothGatt = device.connectGatt(this, false, bluetoothGattCallback);
return true;
} catch (IllegalArgumentException exception) {
Log.w(TAG, "Device not found with provided address. Unable to connect.");
return false;
}
}
}
直播动态
当服务器与 GATT 服务器连接或断开连接时,需要向 activity 通知新状态。您可以通过多种方式来达到这个目的。以下示例使用广播将信息从服务发送到 activity。
服务声明一个函数,用于广播新状态。此函数接受一个操作字符串,该操作字符串会先传递到一个 Intent 对象,然后再广播到系统。
Kotlin
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
Java
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
广播函数可供使用后,可在 BluetoothGattCallback 中使用,以发送与 GATT 服务器的连接状态相关信息。常量和服务的当前连接状态在表示 Intent 操作的服务中声明。
Kotlin
class BluetoothService : Service() {
private var connectionState = STATE_DISCONNECTED
private val bluetoothGattCallback: BluetoothGattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
connectionState = STATE_CONNECTED
broadcastUpdate(ACTION_GATT_CONNECTED)
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
connectionState = STATE_DISCONNECTED
broadcastUpdate(ACTION_GATT_DISCONNECTED)
}
}
}
...
companion object {
const val ACTION_GATT_CONNECTED =
"com.example.bluetooth.le.ACTION_GATT_CONNECTED"
const val ACTION_GATT_DISCONNECTED =
"com.example.bluetooth.le.ACTION_GATT_DISCONNECTED"
private const val STATE_DISCONNECTED = 0
private const val STATE_CONNECTED = 2
}
}
Java
class BluetoothService extends Service {
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";
private static final int STATE_DISCONNECTED = 0;p
private static final int STATE_CONNECTED = 2;
private int connectionState;
...
private final BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
// successfully connected to the GATT Server
connectionState = STATE_CONNECTED;
broadcastUpdate(ACTION_GATT_CONNECTED);
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// disconnected from the GATT Server
connectionState = STATE_DISCONNECTED;
broadcastUpdate(ACTION_GATT_DISCONNECTED);
}
}
};
…
}
监听 activity 中的更新
当服务广播连接更新后,activity 需要实现 BroadcastReceiver。在设置 activity 时注册此接收器,并在 activity 离开屏幕时取消注册此接收器。通过监听来自该服务的事件,activity 可以根据与 BLE 设备的当前连接状态来更新界面。
Kotlin
class DeviceControlActivity : AppCompatActivity() {
...
private val gattUpdateReceiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when (intent.action) {
BluetoothLeService.ACTION_GATT_CONNECTED -> {
connected = true
updateConnectionState(R.string.connected)
}
BluetoothLeService.ACTION_GATT_DISCONNECTED -> {
connected = false
updateConnectionState(R.string.disconnected)
}
}
}
}
override fun onResume() {
super.onResume()
registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter())
if (bluetoothService != null) {
val result = bluetoothService!!.connect(deviceAddress)
Log.d(DeviceControlsActivity.TAG, "Connect request result=$result")
}
}
override fun onPause() {
super.onPause()
unregisterReceiver(gattUpdateReceiver)
}
private fun makeGattUpdateIntentFilter(): IntentFilter? {
return IntentFilter().apply {
addAction(BluetoothLeService.ACTION_GATT_CONNECTED)
addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED)
}
}
}
Java
class DeviceControlsActivity extends AppCompatActivity {
...
private final BroadcastReceiver gattUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
connected = true;
updateConnectionState(R.string.connected);
} else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
connected = false;
updateConnectionState(R.string.disconnected);
}
}
};
@Override
protected void onResume() {
super.onResume();
registerReceiver(gattUpdateReceiver, makeGattUpdateIntentFilter());
if (bluetoothService != null) {
final boolean result = bluetoothService.connect(deviceAddress);
Log.d(TAG, "Connect request result=" + result);
}
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(gattUpdateReceiver);
}
private static IntentFilter makeGattUpdateIntentFilter() {
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
return intentFilter;
}
}
在传输 BLE 数据中,BroadcastReceiver 还可用于传达设备提供的服务发现以及特征数据。
关闭 GATT 连接
处理蓝牙连接时,一个重要步骤是在连接完成后关闭连接。为此,请对 BluetoothGatt 对象调用 close() 函数。在以下示例中,服务保存对 BluetoothGatt 的引用。当 activity 与服务取消绑定时,系统会关闭连接,以避免消耗设备电池电量。
Kotlin
class BluetoothLeService : Service() {
...
override fun onUnbind(intent: Intent?): Boolean {
close()
return super.onUnbind(intent)
}
private fun close() {
bluetoothGatt?.let { gatt ->
gatt.close()
bluetoothGatt = null
}
}
}
Java
class BluetoothService extends Service {
...
@Override
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
private void close() {
if (bluetoothGatt == null) {
Return;
}
bluetoothGatt.close();
bluetoothGatt = null;
}
}
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2023-11-11。