GATT sunucusuna bağlanma

Bir BDE cihazıyla etkileşimde bulunmanın ilk adımı bu cihaza bağlanmaktır. Daha ayrıntılı belirtmek gerekirse, cihazdaki GATT sunucusuna bağlanma. Bir BDE cihazındaki GATT sunucusuna bağlanmak için connectGatt() yöntemini kullanın. Bu yöntem üç parametre alır: bir Context nesnesi, autoConnect (kullanıma sunulur sunulmaz BDE cihazına otomatik olarak bağlanıp bağlanmayacağını belirten bir boole) ve bir BluetoothGattCallback referansı:

Kotlin

var bluetoothGatt: BluetoothGatt? = null
...

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

Java

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

Bu komut, BLE cihazı tarafından barındırılan GATT sunucusuna bağlanır ve daha sonra GATT istemci işlemlerini yürütmek için kullanabileceğiniz bir BluetoothGatt örneği döndürür. Arayan kişi (Android uygulaması) GATT istemcisidir. BluetoothGattCallback, istemciye bağlantı durumu gibi sonuçların yanı sıra diğer GATT istemci işlemlerini göndermek için kullanılır.

Bağlı hizmet kurulumu

Aşağıdaki örnekte BDE uygulaması; Bluetooth cihazlara bağlanmak, cihaz verilerini görüntülemek ve cihazın desteklediği GATT hizmetlerini ve özelliklerini göstermek için bir etkinlik (DeviceControlActivity) sağlar. Kullanıcı girişine göre bu etkinlik, BLE API aracılığıyla BLE cihazıyla etkileşim kuran BluetoothLeService adlı bir Service ile iletişim kurar. İletişim, etkinliğin BluetoothLeService'ye bağlanmasına ve cihazlara bağlanmak için işlevlerin çağrılmasına olanak tanıyan bir bağlı hizmet kullanılarak gerçekleştirilir. BluetoothLeService, etkinlik için hizmete erişim sağlayan bir Binder uygulaması gerektirir.

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

Etkinlik bindService() ile hizmeti başlatabilir, hizmeti başlatmak için bir Intent iletebilir, bağlantı ve bağlantıyı kesme etkinliklerini dinlemek için bir ServiceConnection uygulaması ve ek bağlantı seçeneklerini belirtmek için bir işaret kullanabilir.

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'ı kur

Hizmet bağlandıktan sonra BluetoothAdapter'e erişmesi gerekir. Adaptörün cihazda mevcut olup olmadığı kontrol edilmelidir. BluetoothAdapter hakkında daha fazla bilgi için Bluetooth'u kurma bölümünü okuyun. Aşağıdaki örnekte, bu kurulum kodu, başarılı olduğunu gösteren Boolean değeri döndüren bir initialize() işlevi içinde sarmalanmıştır.

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

    ...
}

Etkinlik, ServiceConnection uygulamasında bu işlevi çağırır. initialize() işlevinden yanlış döndürülen bir değerin işlenmesi uygulamanıza bağlıdır. Kullanıcıya, geçerli cihazın Bluetooth işlemini desteklemediğini belirten bir hata mesajı gösterebilir veya Bluetooth'un çalışması için gereken özellikleri devre dışı bırakabilirsiniz. Aşağıdaki örnekte, kullanıcıyı önceki ekrana geri göndermek için etkinlikte finish() çağrılır.

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

    ...
}

Bir cihaza bağlan

BluetoothLeService örneği başlatıldıktan sonra BDE cihazına bağlanabilir. Etkinliğin, bağlantıyı başlatabilmesi için cihaz adresini hizmete göndermesi gerekir. Hizmet, cihaza erişmek için önce BluetoothAdapter uygulamasında getRemoteDevice() numarasını arayacak. Adaptör bu adrese sahip bir cihaz bulamazsa getRemoteDevice() bir IllegalArgumentException gönderir.

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
}

Hizmet başlatıldıktan sonra DeviceControlActivity, bu connect() işlevini çağırır. Etkinliğin, BDE cihazının adresini iletmesi gerekir. Aşağıdaki örnekte cihaz adresi, etkinliğe ek bir amaç olarak iletilir.

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 geri çağırmasını bildir

Etkinlik, hizmete hangi cihaza bağlanacağını bildirdikten ve hizmet cihaza bağlandıktan sonra, hizmetin BDE cihazındaki GATT sunucusuna bağlanması gerekir. Bu bağlantı; bağlantı durumu, hizmet keşfi, özellik okumaları ve özellik bildirimleri hakkında bildirimleri almak için bir BluetoothGattCallback gerektirir.

Bu konu, bağlantı durumu bildirimlerine odaklanmaktadır. Hizmet keşfi ile özellik okuma işlemlerinin nasıl gerçekleştirileceğini ve özellik bildirimlerini nasıl isteyeceğinizi öğrenmek için BLE verilerini aktarma bölümüne bakın.

Cihazın GATT sunucusuyla bağlantı değiştiğinde onConnectionStateChange() işlevi tetiklenir. Aşağıdaki örnekte, geri çağırma Service sınıfında tanımlanmıştır. Böylece, hizmet ona bağlandığında BluetoothDevice ile kullanılabilir.

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 hizmetine bağlan

BluetoothGattCallback bildirildikten sonra hizmet, cihazdaki GATT hizmetine bağlanmak için connect() işlevindeki BluetoothDevice nesnesini kullanabilir.

connectGatt() işlevi kullanılır. Bu, Context nesnesi, autoConnect boole işareti ve BluetoothGattCallback gerektirir. Bu örnekte, uygulama doğrudan BDE cihazına bağlandığından autoConnect için false iletilmiştir.

BluetoothGatt özelliği de eklenir. Bu sayede hizmet, artık ihtiyaç kalmadığında bağlantıyı kapatabilir.

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

Yayın güncellemeleri

Sunucu, GATT sunucusuna bağlandığında veya sunucuyla olan bağlantısı kesildiğinde, işlemi yeni durumla ilgili olarak bilgilendirmesi gerekir. Bunu yapmanın birkaç yolu vardır. Aşağıdaki örnekte, bilgileri hizmetten etkinliğe göndermek için yayınlar kullanılmaktadır.

Hizmet, yeni durumu yayınlamak için bir işlev bildirir. Bu işlev, sistemde yayınlanmadan önce bir Intent nesnesine iletilen işlem dizesini alır.

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

Yayın işlevi uygulandıktan sonra, GATT sunucusuyla bağlantı durumu hakkında bilgi göndermek için BluetoothGattCallback içinde kullanılır. Sabitler ve hizmetin mevcut bağlantı durumu, Intent işlemlerini temsil eden hizmette bildirilir.

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

    …
}

Etkinlikteki güncellemeleri dinle

Hizmet, bağlantı güncellemelerini yayınladığında etkinliğin bir BroadcastReceiver uygulaması gerekir. Etkinliği ayarlarken bu alıcının kaydını yapın ve etkinlik ekrandan ayrılırken kaydını iptal edin. Hizmetten gelen etkinlikleri dinleyerek etkinlik, BDE cihazıyla mevcut bağlantı durumuna göre kullanıcı arayüzünü güncelleyebilir.

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 verilerini aktar bölümünde BroadcastReceiver, hizmet keşfini ve cihazdaki karakteristik verileri iletmek için de kullanılır.

GATT bağlantısını kapat

Bluetooth bağlantılarıyla çalışırken önemli adımlardan biri, işiniz bittiğinde bağlantıyı kapatmaktır. Bunu yapmak için BluetoothGatt nesnesinde close() işlevini çağırın. Aşağıdaki örnekte hizmet, BluetoothGatt referansını tutmaktadır. Etkinliğin hizmet ile olan bağlantısı kaldırıldığında, cihazın pilinin boşalmasını önlemek için bağlantı kapatılır.

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