Bước đầu tiên trong quá trình tương tác với thiết bị BLE là kết nối với thiết bị. Xem thêm
cụ thể là kết nối với máy chủ GATT trên thiết bị. Cách kết nối với GATT
trên thiết bị BLE, hãy sử dụng
connectGatt()
. Phương thức này nhận 3 tham số:
Đối tượng Context
, autoConnect
(một giá trị boolean
cho biết có tự động kết nối với thiết bị BLE ngay khi thiết bị này hoạt động hay không
có sẵn) và là một tham chiếu đến
BluetoothGattCallback
:
Kotlin
var bluetoothGatt: BluetoothGatt? = null ... bluetoothGatt = device.connectGatt(this, false, gattCallback)
Java
bluetoothGatt = device.connectGatt(this, false, gattCallback);
Nút này sẽ kết nối với máy chủ GATT do thiết bị BLE lưu trữ và trả về một
Thực thể BluetoothGatt
là
sau đó, bạn có thể sử dụng để tiến hành hoạt động ứng dụng GATT. Phương thức gọi (ứng dụng Android)
là ứng dụng khách GATT. Chiến lược phát hành đĩa đơn
BluetoothGattCallback
dùng để cung cấp kết quả cho ứng dụng, chẳng hạn như
trạng thái kết nối cũng như bất kỳ hoạt động máy khách GATT nào khác.
Thiết lập dịch vụ ràng buộc
Trong ví dụ sau, ứng dụng BLE cung cấp một hoạt động
(DeviceControlActivity
) để kết nối với thiết bị Bluetooth, hiển thị dữ liệu thiết bị,
đồng thời hiển thị các dịch vụ và đặc điểm của GATT mà thiết bị hỗ trợ. Dựa trên
khi người dùng nhập, hoạt động này sẽ giao tiếp với
Service
có tên là BluetoothLeService
tương tác với thiết bị BLE thông qua API BLE. Giao tiếp là
được thực hiện bằng dịch vụ ràng buộc cho phép
hoạt động để kết nối với BluetoothLeService
và gọi các hàm tới
kết nối với các thiết bị. BluetoothLeService
cần có một
Phương thức triển khai Binder
cung cấp quyền truy cập vào
dịch vụ cho hoạt động.
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; } } }
Hoạt động có thể bắt đầu dịch vụ bằng
bindService()
!
truyền vào một Intent
để bắt đầu
dịch vụ, ServiceConnection
để theo dõi các sự kiện kết nối và ngắt kết nối, cũng như một cờ
để chỉ định các tuỳ chọn kết nối khác.
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); } }
Thiết lập BluetoothAdapter
Sau khi được liên kết, dịch vụ cần truy cập vào
BluetoothAdapter
. Phải
hãy kiểm tra để đảm bảo bộ chuyển đổi đó có trên thiết bị. Đọc phần Thiết lập
Bluetooth để biết thêm thông tin về
BluetoothAdapter
. Ví dụ sau gói mã thiết lập này trong một
Hàm initialize()
trả về giá trị Boolean
cho biết đã thành công.
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; } ... }
Hoạt động gọi hàm này trong quá trình triển khai ServiceConnection
.
Việc xử lý giá trị trả về sai từ hàm initialize()
phụ thuộc vào
. Bạn có thể hiển thị thông báo lỗi cho người dùng cho biết rằng
thiết bị hiện tại không hỗ trợ hoạt động Bluetooth hoặc tắt bất kỳ tính năng nào
cần có Bluetooth để hoạt động. Trong ví dụ sau đây:
finish()
được gọi trên hoạt động
để đưa người dùng quay lại màn hình trước đó.
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; } }; ... }
Kết nối với thiết bị
Sau khi khởi chạy thực thể BluetoothLeService
, thực thể này có thể kết nối với BLE
thiết bị. Hoạt động cần gửi địa chỉ thiết bị cho dịch vụ để dịch vụ có thể
bắt đầu kết nối. Trước tiên, dịch vụ sẽ gọi
getRemoteDevice()
trên BluetoothAdapter
để truy cập vào thiết bị này. Nếu bộ chuyển đổi không thể tìm thấy
một thiết bị có địa chỉ đó, getRemoteDevice()
sẽ gửi
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
gọi hàm connect()
này sau khi dịch vụ được
đã khởi chạy. Hoạt động cần được truyền vào địa chỉ của thiết bị BLE. Trong
ví dụ sau, địa chỉ thiết bị được chuyển đến hoạt động dưới dạng ý định
khác.
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; } };
Khai báo lệnh gọi lại GATT
Sau khi hoạt động cho dịch vụ biết thiết bị nào cần kết nối và dịch vụ
kết nối với thiết bị, dịch vụ cần kết nối với máy chủ GATT trên
thiết bị BLE. Kết nối này cần có BluetoothGattCallback
để nhận
thông báo về trạng thái kết nối, khám phá dịch vụ, đặc điểm
số lần đọc và các thông báo đặc trưng.
Chủ đề này tập trung vào các thông báo về trạng thái kết nối. Xem Chuyển BLE dữ liệu về cách thực hiện khám phá dịch vụ, đọc đặc điểm và đặc điểm yêu cầu thông báo.
Chiến lược phát hành đĩa đơn
onConnectionStateChange()
được kích hoạt khi kết nối với máy chủ GATT của thiết bị thay đổi.
Trong ví dụ sau, lệnh gọi lại được định nghĩa trong lớp Service
, vì vậy lệnh gọi lại
có thể được sử dụng với
BluetoothDevice
sau khi
dịch vụ nào kết nối với nó.
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 } } };
Kết nối với dịch vụ GATT
Sau khi BluetoothGattCallback
được khai báo, dịch vụ có thể sử dụng
Đối tượng BluetoothDevice
qua hàm connect()
để kết nối với GATT
trên thiết bị.
Chiến lược phát hành đĩa đơn
connectGatt()
được sử dụng. Điều này yêu cầu đối tượng Context
, boolean autoConnect
cờ hiệu và BluetoothGattCallback
. Trong ví dụ này, ứng dụng trực tiếp
kết nối với thiết bị BLE nên false
được truyền cho autoConnect
.
Thuộc tính BluetoothGatt
cũng được thêm vào. Điều này cho phép dịch vụ đóng
kết nối khi không có
cần thiết lâu hơn.
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 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; } } }
Tin cập nhật về chương trình phát sóng
Khi kết nối hoặc ngắt kết nối khỏi máy chủ GATT, máy chủ cần thông báo hoạt động của trạng thái mới. Có một vài cách để thực hiện việc này. Chiến lược phát hành đĩa đơn trong ví dụ sau đây sử dụng thông báo truyền tin để gửi thông tin từ dịch vụ đến hoạt động.
Dịch vụ khai báo một hàm để truyền trạng thái mới. Hàm này nhận
trong chuỗi hành động được truyền đến đối tượng Intent
trước khi được truyền
cho hệ thống.
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); }
Sau khi đặt chức năng truyền tin, chức năng này sẽ được dùng trong
BluetoothGattCallback
để gửi thông tin về trạng thái kết nối với
máy chủ GATT. Hằng số và trạng thái kết nối hiện tại của dịch vụ được khai báo
trong dịch vụ biểu thị các hành động Intent
.
Kotlin
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 } }
Java
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); } } }; … }
Nghe thông tin cập nhật về hoạt động
Sau khi dịch vụ truyền tin cập nhật kết nối, hoạt động cần
triển khai BroadcastReceiver
.
Đăng ký receiver này khi thiết lập hoạt động và huỷ đăng ký khi
đang rời khỏi màn hình. Bằng cách lắng nghe các sự kiện từ dịch vụ,
hoạt động có thể cập nhật giao diện người dùng dựa trên
trạng thái kết nối với thiết bị 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; } }
Trong phần Chuyển dữ liệu BLE,
BroadcastReceiver
cũng được dùng để truyền thông tin về việc khám phá dịch vụ
cũng như dữ liệu đặc trưng từ thiết bị.
Đóng kết nối GATT
Một bước quan trọng khi xử lý kết nối Bluetooth là đóng
khi bạn đã hoàn tất với nó. Để thực hiện việc này, hãy gọi close()
trên đối tượng BluetoothGatt
. Trong ví dụ sau, dịch vụ
chứa tham chiếu đến BluetoothGatt
. Khi hoạt động huỷ liên kết với
dịch vụ, kết nối sẽ được đóng để tránh làm tiêu hao pin thiết bị.
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 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; } }