Terhubung ke server GATT

Langkah pertama dalam berinteraksi dengan perangkat BLE adalah terhubung ke perangkat tersebut. Selengkapnya khususnya, menghubungkan ke server GATT di perangkat. Untuk terhubung ke GATT server pada perangkat BLE, gunakan connectGatt() . Metode ini menggunakan tiga parameter: Objek Context, autoConnect (boolean menunjukkan apakah akan terhubung secara otomatis ke perangkat BLE segera setelah tersedia), dan referensi ke BluetoothGattCallback:

KotlinJava
var bluetoothGatt: BluetoothGatt? = null
...

bluetoothGatt
= device.connectGatt(this, false, gattCallback)
bluetoothGatt = device.connectGatt(this, false, gattCallback);

Ini terhubung ke server GATT yang dihosting oleh perangkat BLE, dan menampilkan BluetoothGatt, yang yang dapat Anda gunakan untuk melakukan operasi klien GATT. Penelepon (aplikasi Android) adalah klien GATT. Tujuan BluetoothGattCallback digunakan untuk memberikan hasil kepada klien, seperti status koneksi, serta operasi klien GATT lebih lanjut.

Menyiapkan layanan terikat

Pada contoh berikut, aplikasi BLE menyediakan aktivitas (DeviceControlActivity) untuk menghubungkan ke perangkat Bluetooth, menampilkan data perangkat, dan menampilkan layanan dan karakteristik GATT yang didukung oleh perangkat. Berdasarkan berdasarkan input pengguna, aktivitas ini berkomunikasi dengan Service disebut BluetoothLeService, yang berinteraksi dengan perangkat BLE melalui BLE API. Komunikasinya dijalankan menggunakan layanan terikat yang memungkinkan aktivitas untuk terhubung ke BluetoothLeService dan memanggil fungsi untuk menghubungkan ke perangkat. BluetoothLeService memerlukan Implementasi Binder yang memberikan akses ke layanan untuk aktivitas tersebut.

KotlinJava
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;
       
}
   
}
}

Aktivitas ini dapat memulai layanan menggunakan bindService(), yang meneruskan Intent untuk memulai layanan, sebuah ServiceConnection untuk memproses peristiwa koneksi dan pemutusan koneksi, serta sebuah flag untuk menentukan opsi koneksi tambahan.

KotlinJava
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);
   
}
}

Menyiapkan BluetoothAdapter

Setelah layanan terikat, layanan perlu mengakses BluetoothAdapter Seharusnya periksa apakah adaptor tersedia di perangkat. Baca Menyiapkan Bluetooth untuk mengetahui informasi selengkapnya tentang BluetoothAdapter. Contoh berikut menggabungkan kode penyiapan ini dalam Fungsi initialize() yang menampilkan nilai Boolean yang menunjukkan keberhasilan.

KotlinJava
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;
   
}

   
...
}

Aktivitas ini memanggil fungsi ini dalam implementasi ServiceConnection-nya. Penanganan nilai hasil yang salah dari fungsi initialize() bergantung pada aplikasi. Anda dapat menampilkan pesan {i>error<i} kepada pengguna yang menunjukkan bahwa perangkat saat ini tidak mendukung operasi Bluetooth atau menonaktifkan fitur apa pun yang membutuhkan {i>Bluetooth<i} agar dapat berfungsi. Dalam contoh berikut, finish() dipanggil pada aktivitas untuk mengirim pengguna kembali ke layar sebelumnya.

KotlinJava
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;
       
}
   
};

   
...
}

Hubungkan ke perangkat

Setelah instance BluetoothLeService diinisialisasi, instance tersebut dapat terhubung ke BLE perangkat seluler. Aktivitas tersebut perlu mengirimkan alamat perangkat ke layanan agar dapat memulai koneksi. Layanan ini akan memanggil terlebih dahulu getRemoteDevice() di BluetoothAdapter untuk mengakses perangkat. Jika adaptor tidak dapat menemukan perangkat dengan alamat tersebut, getRemoteDevice() akan menampilkan IllegalArgumentException.

KotlinJava
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 memanggil fungsi connect() ini setelah layanan diinisialisasi. Aktivitas ini perlu meneruskan alamat perangkat BLE. Di beberapa contoh berikut, alamat perangkat diteruskan ke aktivitas sebagai intent tambahan.

KotlinJava
// 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;
   
}
};

Mendeklarasikan callback GATT

Setelah aktivitas memberi tahu layanan perangkat mana yang harus dihubungkan dan layanan terhubung ke perangkat, layanan harus terhubung ke server GATT di perangkat BLE. Koneksi ini memerlukan BluetoothGattCallback untuk menerima notifikasi tentang status koneksi, penemuan layanan, karakteristik pembacaan, dan notifikasi karakteristik.

Topik ini berfokus pada notifikasi status koneksi. Lihat Mentransfer BLE data untuk mengetahui cara penemuan layanan, pembacaan karakteristik, dan karakteristik permintaan notifikasi.

Tujuan onConnectionStateChange() akan dipicu saat koneksi ke server GATT perangkat berubah. Pada contoh berikut, callback didefinisikan dalam class Service sehingga dapat digunakan dengan BluetoothDevice setelah terhubung ke sana.

KotlinJava
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
       
}
   
}
};

Menghubungkan ke layanan GATT

Setelah BluetoothGattCallback dideklarasikan, layanan dapat menggunakan metode Objek BluetoothDevice dari fungsi connect() untuk terhubung ke GATT layanan penyimpanan pada perangkat.

Tujuan connectGatt() digunakan. Memerlukan objek Context, boolean autoConnect penanda, dan BluetoothGattCallback. Dalam contoh ini, aplikasi secara langsung menghubungkan ke perangkat BLE, sehingga false diteruskan untuk autoConnect.

Properti BluetoothGatt juga ditambahkan. Hal ini memungkinkan layanan menutup koneksi jaringan saat tidak ada koneksi dibutuhkan lagi.

KotlinJava
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;
       
}
   
}
}

Update siaran

Ketika server terhubung atau terputus dari server GATT, server perlu memberi tahu aktivitas status yang baru. Ada beberapa cara untuk melakukannya. Tujuan contoh berikut menggunakan broadcast untuk mengirim informasi dari layanan ke aktivitas.

Layanan mendeklarasikan fungsi untuk menyiarkan status baru. Fungsi ini menggunakan dalam string tindakan yang diteruskan ke objek Intent sebelum disiarkan ke sistem.

KotlinJava
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);
}

Setelah fungsi siaran diterapkan, fungsi ini akan digunakan dalam BluetoothGattCallback untuk mengirim informasi tentang status koneksi dengan server GATT. Konstanta dan status koneksi layanan saat ini dideklarasikan di layanan yang mewakili tindakan Intent.

KotlinJava
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);
           
}
       
}
   
};

   

}

Memproses pembaruan dalam aktivitas

Setelah layanan menyiarkan pembaruan koneksi, aktivitas perlu mengimplementasikan BroadcastReceiver. Daftarkan penerima ini saat menyiapkan aktivitas, dan batalkan pendaftarannya ketika aktivitas meninggalkan layar. Dengan mendengarkan peristiwa dari layanan, aktivitas tersebut dapat memperbarui antarmuka pengguna berdasarkan status koneksi Anda dengan perangkat BLE.

KotlinJava
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;
   
}
}

Di bagian Transfer data BLE, BroadcastReceiver juga digunakan untuk mengomunikasikan penemuan layanan sebagai serta data karakteristik dari perangkat.

Tutup koneksi GATT

Salah satu langkah penting ketika berhadapan dengan koneksi {i>Bluetooth<i} adalah menutup koneksi ketika Anda sudah selesai dengan itu. Untuk melakukannya, panggil close() fungsi pada objek BluetoothGatt. Dalam contoh berikut, layanan menyimpan referensi ke BluetoothGatt. Saat aktivitas terlepas dari , koneksi akan ditutup untuk menghindari pengurasan baterai perangkat.

KotlinJava
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;
     
}
}