与 BLE 设备交互的第一步是连接该设备。更多
具体来说就是连接到设备上的 GATT 服务器。关联 GATT
服务器,请使用
connectGatt()
方法。此方法采用三个参数:
Context
对象,autoConnect
(一个布尔值)
指示是否在 BLE 设备完成后立即自动连接到
可用),并且引用了
BluetoothGattCallback
:
var bluetoothGatt: BluetoothGatt? = null
...
bluetoothGatt = device.connectGatt(this, false, gattCallback)
bluetoothGatt = device.connectGatt(this, false, gattCallback);
这将连接到由 BLE 设备托管的 GATT 服务器,并返回
BluetoothGatt
实例,
然后,您可以使用它执行 GATT 客户端操作。调用方(Android 应用)
是 GATT 客户端通过
BluetoothGattCallback
用于向客户端传递结果,例如
连接状态,以及任何进一步的 GATT 客户端操作。
设置绑定服务
在以下示例中,BLE 应用提供了一个 activity
(DeviceControlActivity
) 可连接到蓝牙设备、显示设备数据、
并显示设备支持的 GATT 服务和特征。位于
该活动会与
Service
调用了 BluetoothLeService
,
通过 BLE API 与 BLE 设备进行交互。沟通是
使用绑定服务执行,这样,
要连接到 BluetoothLeService
并调用函数的 activity
连接到设备BluetoothLeService
需要
Binder
实现,可提供对
服务。
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
}
}
}
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
用于监听连接和断开连接事件的实现,以及一个用于
以指定其他连接选项。
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)
}
}
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
值。
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
}
...
}
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()
可将用户带回上一屏幕。
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
}
}
...
}
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;
}
};
...
}
连接到设备
初始化 BluetoothLeService
实例后,它可以连接到 BLE
设备。activity 需要将设备地址发送给服务,然后才能
发起连接。该服务将首先调用
getRemoteDevice()
在 BluetoothAdapter
上访问该设备。如果适配器找不到
在具有该地址的设备时,getRemoteDevice()
会抛出一个
IllegalArgumentException
。
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
}
}
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 传递给 activity
extra。
// 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
}
}
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 设备。此连接需要 BluetoothGattCallback
才能接收
有关连接状态、服务发现、特征
读取和特征通知
本主题重点介绍连接状态通知。请参阅传输 BLE 数据来了解如何 服务发现、特征读取和请求特征 通知。
通过
onConnectionStateChange()
函数。
在以下示例中,回调是在 Service
类中定义的,因此它
可以搭配
BluetoothDevice
发生
服务就会连接到该网络。
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
}
}
}
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
属性。这样,该服务就可以关闭
。
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
}
}
}
class BluetoothLeService 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
对象
发送到系统。
private fun broadcastUpdate(action: String) {
val intent = Intent(action)
sendBroadcast(intent)
}
private void broadcastUpdate(final String action) {
final Intent intent = new Intent(action);
sendBroadcast(intent);
}
广播函数设置好后,即可用在
BluetoothGattCallback
,用于发送
GATT 服务器。声明常量和服务的当前连接状态
。Intent
class BluetoothLeService : 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
}
}
class BluetoothLeService 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;
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 需要
实现 BroadcastReceiver
。
在设置 activity 时注册此接收器,并在
activity 正在离开屏幕。通过监听来自该服务的事件,
activity 能够根据当前的
BLE 设备的连接状态。
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)
}
}
}
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 连接
处理蓝牙连接时,一个重要的步骤是关闭
使用它们。为此,请调用 close()
针对 BluetoothGatt
对象调用函数。在以下示例中,服务
存储对 BluetoothGatt
的引用。当 activity 与
服务,连接会关闭,以免消耗设备电池电量。
class BluetoothLeService : Service() {
...
override fun onUnbind(intent: Intent?): Boolean {
close()
return super.onUnbind(intent)
}
private fun close() {
bluetoothGatt?.let { gatt ->
gatt.close()
bluetoothGatt = null
}
}
}
class BluetoothLeService extends Service {
...
@Override
public boolean onUnbind(Intent intent) {
close();
return super.onUnbind(intent);
}
private void close() {
if (bluetoothGatt == null) {
Return;
}
bluetoothGatt.close();
bluetoothGatt = null;
}
}