Mobil dünyada cihazlar arası iletişimin temel taşlarından Bluetooth, Android uygulamalarında kritik bir rol oynar. Akıllı cihazlarımızın vazgeçilmezi haline gelen bu teknolojiyle neler yapabileceğimizi, sıfırdan başlayarak adım adım keşfedelim.
Günümüzün birbirine bağlı dünyasında, cihazlar arası kablosuz iletişim, kullanıcı deneyiminin vazgeçilmez bir parçasıdır. İşte tam da bu noktada Bluetooth teknolojisi devreye girer. Android ekosisteminde Bluetooth, akıllı telefonlarımızdan tabletlerimize, hatta giyilebilir teknolojilere kadar geniş bir yelpazede, düşük güçlü ve kısa mesafeli veri alışverişi için standart bir yöntem sunar. Öncelikle, Bluetooth’un temel işlevlerini ve Android uygulamaları için neden bu kadar kritik olduğunu anlamakla başlayalım. Bluetooth, genellikle iki ana modda çalışır: Klasik Bluetooth (Bluetooth Classic) ve Bluetooth Düşük Enerji (Bluetooth Low Energy – BLE).
Klasik Bluetooth, genellikle daha yüksek bant genişliği gerektiren ses akışı (kulaklıklar, hoparlörler) veya büyük dosya transferleri gibi uygulamalar için tercih edilir. A2DP (Advanced Audio Distribution Profile) ve HFP (Hands-Free Profile) gibi profiller, klasik Bluetooth’un yaygın kullanım alanlarını oluşturur. Diğer yandan, BLE, adından da anlaşılacağı gibi, çok düşük güç tüketimiyle karakterizedir ve IoT (Nesnelerin İnterneti) cihazları, akıllı sensörler, fitness takipçileri ve akıllı ev aksesuarları gibi uygulamalar için idealdir. Bu iki modun farklı kullanım senaryolarına sahip olması, Android geliştiricilerine geniş bir esneklik sunar.
Bluetooth’un Android uygulamaları için önemi saymakla bitmez. Örneğin, bir akıllı ev uygulaması düşünün; termostatınızla veya aydınlatma sisteminizle doğrudan etkileşim kurmak için BLE kullanılabilir. Ayrıca, sağlık ve fitness uygulamaları, kalp atış hızı monitörleri veya adım sayarları gibi giyilebilir cihazlardan veri toplamak için yine BLE’ye başvurur. Klasik Bluetooth ise, araç içi eğlence sistemleriyle veya başka bir Android cihazla hızlıca büyük dosyalar paylaşmak gibi senaryolarda hayat kurtarıcı olabilir. Bu teknoloji sayesinde, uygulamalarımız fiziksel dünyayla daha derinlemesine etkileşim kurabilir, kullanıcı deneyimini zenginleştirebilir ve yeni işlevsellikler sunabilir. Bu geniş kullanım alanı, Android geliştiricilerinin Bluetooth yeteneklerini iyi bilmesini zorunlu kılar. İzin yönetimi, cihaz keşfi, güvenli bağlantı kurulumu ve veri transferi gibi konular, başarılı bir Bluetooth entegrasyonu için temel taşları oluşturur. Tüm bu nedenlerle, Android geliştirme yolculuğunuzda Bluetooth’u anlamak ve etkin bir şekilde kullanabilmek, uygulamanızın potansiyelini önemli ölçüde artıracaktır.
Android Uygulamalarında Bluetooth Nasıl Etkinleştirilir ve Cihazlar Nasıl Keşfedilir?
Android uygulamanızda Bluetooth özelliklerini kullanmaya başlamadan önce atılması gereken ilk ve en önemli adımlar, gerekli izinleri talep etmek, Bluetooth adaptörünü etkinleştirmek ve çevredeki cihazları keşfetmektir. Bu süreçler, uygulamanızın cihazlar arası iletişim kurabilmesi için temel oluşturur ve hatasız bir şekilde yapılandırılmalıdır. İzinler, Android 6.0 (API 23) ve sonrası için özellikle önem arz eder, çünkü kullanıcıdan çalışma zamanında (runtime) onay alınması gerekir.
Öncelikle, uygulamanızın Bluetooth donanımına erişebilmesi için manifest dosyanızda şu izinleri belirtmeniz gerekir:
BLUETOOTH ve BLUETOOTH_ADMIN Klasik Bluetooth için temel izinlerdir. Android 10 (API 29) ve öncesinde cihaz keşfi için ACCESS_FINE_LOCATION veya ACCESS_COARSE_LOCATION izni gereklidir, çünkü Bluetooth taraması konum bilgisi sağlayabilir. Android 12 (API 31) ve sonrası için ise BLUETOOTH_SCAN, BLUETOOTH_ADVERTISE ve BLUETOOTH_CONNECT izinleri tanıtılmıştır. Bu yeni izinler, konum iznine gerek kalmadan Bluetooth işlevselliğini kullanmanıza olanak tanır, ancak yine de çalışma zamanında kullanıcı onayı gerektirir.
İzinleri manifest dosyanıza ekledikten sonra, uygulamanızın çalışma zamanında bu izinleri istemesi gerekir. Örneğin, bir etkinlik (Activity) içinde şu şekilde kontrol edebilirsiniz:
private val REQUEST_ENABLE_BT = 1
private val REQUEST_BLUETOOTH_PERMISSIONS = 2
fun checkBluetoothPermissions() {
val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
// Bluetooth'un cihazda desteklenip desteklenmediğini kontrol et
if (bluetoothAdapter == null) {
// Cihaz Bluetooth'u desteklemiyor
Toast.makeText(this, "Bu cihaz Bluetooth'u desteklemiyor.", Toast.LENGTH_LONG).show()
return
}
// Bluetooth kapalıysa açma isteği gönder
if (!bluetoothAdapter.isEnabled) {
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
}
// Gerekli çalışma zamanı izinlerini kontrol et ve iste (API 31+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val permissions = arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE
)
val deniedPermissions = permissions.filter {
ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
}
if (deniedPermissions.isNotEmpty()) {
ActivityCompat.requestPermissions(this, deniedPermissions.toTypedArray(), REQUEST_BLUETOOTH_PERMISSIONS)
}
} else { // API 30 ve altı için konum izni kontrolü
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_BLUETOOTH_PERMISSIONS)
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(this, "Bluetooth başarıyla açıldı.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Bluetooth açılamadı.", Toast.LENGTH_SHORT).show()
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_BLUETOOTH_PERMISSIONS) {
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
Toast.makeText(this, "Tüm Bluetooth izinleri verildi.", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Bazı Bluetooth izinleri reddedildi.", Toast.LENGTH_LONG).show()
}
}
}
Bluetooth adaptörü etkinleştirildikten ve gerekli izinler alındıktan sonra, çevredeki cihazları keşfetmeye başlayabilirsiniz. Klasik Bluetooth için cihaz keşfi, adaptörün belirli bir süre boyunca tarama yapmasıyla gerçekleşir. Keşfedilen cihazlar, bir BroadcastReceiver aracılığıyla yakalanır.
private val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
private val discoveredDevices = mutableListOf()
// Cihaz keşfini dinlemek için BroadcastReceiver
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
when(intent.action) {
BluetoothDevice.ACTION_FOUND -> {
val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
device?.let {
if (!discoveredDevices.contains(it)) {
discoveredDevices.add(it)
// Cihaz adını ve adresini loglayabilir veya UI'a ekleyebilirsiniz
Log.d("Bluetooth", "Cihaz bulundu: ${it.name} - ${it.address}")
}
}
}
BluetoothAdapter.ACTION_DISCOVERY_FINISHED -> {
Log.d("Bluetooth", "Cihaz keşfi tamamlandı.")
// UI'ı güncelleyebilir veya taramayı durdurabilirsiniz
}
}
}
}
fun startDiscovery() {
// BluetoothAdaptörünün null olmadığını ve etkin olduğunu kontrol edin
bluetoothAdapter?.let {
if (!it.isEnabled) {
// Bluetooth etkin değilse, kullanıcıdan açmasını isteyin
val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
return
}
// Discovery devam ediyorsa önce iptal et
if (it.isDiscovering) {
it.cancelDiscovery()
}
// BroadcastReceiver'ı kaydet
val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)
registerReceiver(receiver, filter)
// Keşfi başlat
val success = it.startDiscovery()
if (success) {
Toast.makeText(this, "Cihaz keşfi başlatıldı...", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(this, "Cihaz keşfi başlatılamadı.", Toast.LENGTH_SHORT).show()
}
}
}
override fun onDestroy() {
super.onDestroy()
// Receiver'ı kayıttan çıkar
unregisterReceiver(receiver)
// Discovery devam ediyorsa iptal et
bluetoothAdapter?.cancelDiscovery()
}
Bu adımlar, uygulamanızın Bluetooth iletişimine başlaması için temel hazırlığı sağlar. Cihaz keşfi genellikle yaklaşık 12 saniye sürer ve bu süre zarfında CPU yoğun çalışır, bu nedenle uygulamanızın arayüzünün donmaması için bu işlemleri genellikle bir arka plan iş parçacığında (thread) veya Coroutine içinde yapmanız önerilir. Ayrıca, tarama işlemi tamamlandığında veya uygulamanız kapanmadan önce cancelDiscovery() çağrısını yaparak pil tüketimini optimize etmeyi unutmayın. Bu şekilde, kullanıcı dostu ve performansı yüksek bir Bluetooth uygulaması için sağlam bir temel oluşturmuş olursunuz.
Uzman İpucu: Android 12 (API 31) ve üzeri için Bluetooth izinlerini yönetirken, BLUETOOTH_SCAN iznini isterken android:usesPermissionFlags="neverForLocation" niteliğini kullanmak, konum izni gerekliliğini atlayarak kullanıcı gizliliğini artırabilir ve izin diyaloglarını basitleştirebilirsiniz. Bu sayede, uygulamanızın sadece Bluetooth işlevi için izin istediği daha net belirtilir.
Bluetooth Klasik ile Güvenli Bağlantı Kurma ve Veri Transferi
Cihaz keşfini tamamladıktan ve hedef cihazı belirledikten sonraki adım, iki cihaz arasında güvenli bir bağlantı kurmak ve veri transferini gerçekleştirmektir. Klasik Bluetooth, genellikle bir istemci (client) ve bir sunucu (server) mimarisini kullanır. Bir cihaz bir sunucu soketi açarak gelen bağlantıları beklerken, diğer cihaz bir istemci soketi aracılığıyla bu sunucuya bağlanmaya çalışır. Bu süreç, genellikle bir UUID (Universally Unique Identifier) kullanılarak belirli bir servis profilinin hedeflenmesiyle gerçekleşir.
Sunucu tarafında, bir BluetoothServerSocket oluşturulur ve listenUsingRfcommWithServiceRecord() metodu ile gelen bağlantılar beklenir. Bu metod, bir servis adı ve bir UUID alır. UUID, uygulamanızın sağladığı servis için benzersiz bir tanımlayıcı olmalıdır ve hem sunucu hem de istemci tarafında aynı UUID kullanılmalıdır. Sunucu, accept() metodunu çağırarak bir bağlantıyı bloke edici bir şekilde bekler. Bir bağlantı kurulduğunda, bu metod bir BluetoothSocket döndürür. İşte sunucu tarafı için basit bir örnek:
private inner class AcceptThread(private val bluetoothAdapter: BluetoothAdapter) : Thread() {
private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
bluetoothAdapter.listenUsingRfcommWithServiceRecord("MyAppName", MY_UUID)
}
override fun run() {
var socket: BluetoothSocket? = null
// Bağlantıyı kabul etme döngüsü. Bir bağlantı kabul edilirse veya bir hata oluşursa döngüden çıkılır.
while (true) {
try {
socket = mmServerSocket?.accept() // Bloklayıcı çağrı
} catch (e: IOException) {
Log.e(TAG, "Socket'in kabulü başarısız oldu", e)
break
}
socket?.also {
// Bağlantı başarılı oldu, yeni bir thread'de veri transferini yönet
manageConnectedSocket(it)
mmServerSocket?.close()
return
}
}
}
fun cancel() {
try {
mmServerSocket?.close()
} catch (e: IOException) {
Log.e(TAG, "ServerSocket kapatılamadı", e)
}
}
}
İstemci tarafında ise, hedef BluetoothDevice nesnesi üzerinden createRfcommSocketToServiceRecord() metodu çağırılarak bir BluetoothSocket oluşturulur. Ardından, connect() metodu çağırılarak sunucuya bağlanılmaya çalışılır. Bu metod da bloklayıcıdır ve arka plan iş parçacığında çalıştırılması önemlidir. İşte istemci tarafı için bir örnek:
private inner class ConnectThread(private val device: BluetoothDevice, private val bluetoothAdapter: BluetoothAdapter) : Thread() {
private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
device.createRfcommSocketToServiceRecord(MY_UUID)
}
override fun run() {
// Keşif sırasında bağlanmaya çalışırken keşfi iptal edin
bluetoothAdapter.cancelDiscovery()
mmSocket?.let { socket ->
try {
// Sunucuya bağlan. Bu bloklayıcı bir çağrıdır ve başarılı olduğunda geri döner
socket.connect()
} catch (e: IOException) {
// Bağlantı başarısız oldu, soketi kapat ve çık
try {
socket.close()
} catch (e2: IOException) {
Log.e(TAG, "Soket kapatılamadı", e2)
}
return
}
// Bağlantı başarılı oldu, yeni bir thread'de veri transferini yönet
manageConnectedSocket(socket)
}
}
fun cancel() {
try {
mmSocket?.close()
} catch (e: IOException) {
Log.e(TAG, "Soket kapatılamadı", e)
}
}
}
Bağlantı her iki tarafta da başarıyla kurulduktan sonra, BluetoothSocket nesnesinden getInputStream() ve getOutputStream() metotları aracılığıyla veri akışları elde edilir. Bu akışlar kullanılarak veriler gönderilip alınabilir. Veri transferi için özel bir iş parçacığı oluşturmak ve bu akışları dinlemek veya bunlara yazmak, uygulamanın ana UI iş parçacığının engellenmemesi için hayati öneme sahiptir. Veri transferi örneği aşağıdaki gibidir:
private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {
private val mmInStream: InputStream = mmSocket.inputStream
private val mmOutStream: OutputStream = mmSocket.outputStream
private val mmBuffer: ByteArray = ByteArray(1024) // Veri okumak için tampon
override fun run() {
var numBytes: Int // Okunan bayt sayısı
while (true) {
try {
// Giriş akışından oku
numBytes = mmInStream.read(mmBuffer)
// Okunan baytları ana aktiviteye gönder (örneğin bir Handler kullanarak)
val readMessage = String(mmBuffer, 0, numBytes)
Log.d(TAG, "Alınan mesaj: $readMessage")
// handler.obtainMessage(MESSAGE_READ, numBytes, -1, mmBuffer).sendToTarget()
} catch (e: IOException) {
Log.d(TAG, "Giriş akışı kesildi", e)
break
}
}
}
// Harici aktiviteye çıkış akışına yazma çağrısı
fun write(bytes: ByteArray) {
try {
mmOutStream.write(bytes)
Log.d(TAG, "Gönderilen mesaj: ${String(bytes)}")
// handler.obtainMessage(MESSAGE_WRITE, -1, -1, bytes).sendToTarget()
} catch (e: IOException) {
Log.e(TAG, "Çıkış akışına yazılırken hata oluştu", e)
}
}
// Bağlantıyı kapat
fun cancel() {
try {
mmSocket.close()
} catch (e: IOException) {
Log.e(TAG, "Soket kapatılırken hata oluştu", e)
}
}
}
Her üç thread de (AcceptThread, ConnectThread, ConnectedThread) bağlantının ve veri transferinin belirli aşamalarını yönetmek için tasarlanmıştır. manageConnectedSocket(BluetoothSocket) fonksiyonu, bağlantı kurulduktan sonra ConnectedThread'i başlatmak ve yönetmek için kullanılır. Bu yapı, Klasik Bluetooth ile iki Android cihaz arasında güvenli ve istikrarlı bir şekilde veri alışverişi yapmanızı sağlar. Bu işlemler sırasında oluşabilecek hataları (örneğin, bağlantı kesilmesi) doğru bir şekilde yönetmek, uygulamanızın sağlamlığı açısından kritik öneme sahiptir.
Uzman İpucu: Bluetooth iletişiminde kullanılan UUID'lerin, uygulamanıza özel ve benzersiz olduğundan emin olun. Kendi UUID'nizi oluşturmak için çevrimiçi UUID jeneratörlerini kullanabilirsiniz. Aynı UUID'nin hem sunucu hem de istemci tarafında birebir aynı olması, bağlantının başarılı olması için şarttır.
Vaka Analizi: İki Android Cihaz Arasında Dosya Paylaşımı
Klasik Bluetooth'un en pratik kullanım senaryolarından biri, iki Android cihaz arasında dosya paylaşımıdır. Bu senaryoyu uygulamak için yukarıda bahsedilen sunucu-istemci mimarisini ve veri akışlarını kullanarak bir dosyanın baytlarını gönderebiliriz. Diyelim ki A cihazı bir fotoğrafı B cihazına göndermek istiyor. A cihazı istemci rolünü üstlenirken, B cihazı sunucu olarak hareket edecektir.
B Cihazı (Sunucu):
- Bluetooth adaptörünü etkinleştirir ve gerekli izinleri alır.
AcceptThread'i başlatarak gelen bağlantı isteğini dinler.
- A cihazından gelen bir bağlantıyı kabul ettiğinde,
BluetoothSocket nesnesini alır.
ConnectedThread'i başlatır ve gelen InputStream'den dosya baytlarını okur.
- Okuduğu baytları bir dosyaya yazar (örneğin, uygulamanın özel depolama alanına veya ortak bir medya dizinine).
- Dosya transferi tamamlandığında bağlantıyı kapatır.
A Cihazı (İstemci):
- Bluetooth adaptörünü etkinleştirir ve gerekli izinleri alır.
- Cihaz keşfini başlatır ve B cihazını bulur.
- B cihazına bağlanmak için
ConnectThread'i başlatır, sunucunun UUID'sini kullanarak bir BluetoothSocket oluşturur ve bağlanır.
- Bağlantı başarılı olduğunda, gönderilecek dosyanın
InputStream'ini elde eder.
ConnectedThread'in write() metodunu kullanarak dosyanın baytlarını parça parça OutputStream'e yazar.
- Dosya transferi tamamlandığında bağlantıyı kapatır.
Bu senaryoda, dosya transferinin güvenilirliği için bazı ek hususlar önemlidir. Örneğin, dosyanın toplam boyutunu transferden önce karşı tarafa bildirmek, alıcı cihazın ne kadar veri beklediğini bilmesini sağlar. Ayrıca, her bir veri parçasının başarıyla alınıp alınmadığını teyit etmek için basit bir onay mekanizması (ACK/NACK) uygulanabilir. Büyük dosyalar için, transferin ilerleyişini kullanıcıya göstermek (progress bar) kullanıcı deneyimini iyileştirecektir. Ayrıca, dosya adının da transferden önce gönderilmesi, alıcı cihazın dosyayı doğru adla kaydetmesini sağlar. Bu vaka analizi, Klasik Bluetooth'un dosya paylaşımı gibi pratik ve yaygın kullanım senaryolarında nasıl kullanılabileceğini göstermektedir.
Bluetooth Düşük Enerji (BLE) ile Modern Uygulamalar Geliştirme
Klasik Bluetooth'un yanı sıra, Bluetooth Düşük Enerji (BLE), modern Android uygulamalarında giderek daha fazla önem kazanmaktadır. Özellikle Nesnelerin İnterneti (IoT) cihazları, giyilebilir teknolojiler ve akıllı sensörler gibi düşük güç tüketimi gerektiren senaryolar için BLE, ideal bir çözümdür. BLE, Klasik Bluetooth'a kıyasla daha hızlı bağlantı süreleri, daha basit profiller ve en önemlisi çok daha düşük enerji tüketimi sunar. Bu bölümde, Android'de BLE geliştirmenin temel taşlarını, GATT profillerini ve örnek bir BLE tarama ve bağlantı akışını inceleyeceğiz.
BLE'nin kalbinde GATT (Generic Attribute Profile) yatar. GATT, BLE cihazlarının veri alışverişi yapma şeklini tanımlayan bir yapıdır. Her BLE cihazı, bir GATT sunucusu olarak davranabilir ve servisler (Services) ve karakteristikler (Characteristics) adı verilen yapılandırılmış verileri sunar. Servisler, belirli bir işlevselliği gruplandırır (örneğin, Kalp Atış Hızı Servisi). Her servis içinde ise, karakteristikler bulunur; bunlar, servis ile ilgili tek bir veri noktasını temsil eder (örneğin, kalp atış hızı değeri). Karakteristikler ayrıca açıklayıcılar (Descriptors) içerebilir, bu da karakteristik hakkında ek bilgi sağlar. Bu hiyerarşik yapı, BLE cihazları arasındaki veri iletişimini standartlaştırır.
Android'de BLE ile çalışmak için, öncelikle Bluetooth adaptörünü ve izinleri yukarıda Klasik Bluetooth için anlatıldığı gibi etkinleştirmelisiniz. Ek olarak, BLE taraması için BluetoothLeScanner sınıfını kullanırız. Bu sınıf, çevredeki BLE cihazlarını asenkron olarak taramak için güçlü filtreleme yetenekleri sunar. Tarama sonuçları bir ScanCallback üzerinden alınır:
private val bluetoothLeScanner: BluetoothLeScanner? by lazy {
bluetoothAdapter?.bluetoothLeScanner
}
private val scanSettings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()
private val leScanCallback: ScanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
super.onScanResult(callbackType, result)
result.device?.let { device ->
// Cihazı listede olup olmadığını kontrol et ve ekle
Log.d("BLE", "BLE cihazı bulundu: ${device.name ?: "Bilinmeyen"} - ${device.address}")
// Cihazı bir RecyclerView'e ekleyebilirsiniz
}
}
override fun fun onBatchScanResults(results: MutableList?) {
super.onBatchScanResults(results)
results?.forEach { result ->
result.device?.let { device ->
Log.d("BLE", "Batch BLE cihazı bulundu: ${device.name ?: "Bilinmeyen"} - ${device.address}")
}
}
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
Log.e("BLE", "BLE taraması başarısız oldu: $errorCode")
Toast.makeText(this@MainActivity, "BLE taraması başarısız oldu.", Toast.LENGTH_SHORT).show()
}
}
fun startBleScan() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)) {
// İzinleri iste
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.BLUETOOTH_SCAN, Manifest.permission.BLUETOOTH_CONNECT), REQUEST_BLUETOOTH_PERMISSIONS)
return
}
// İzinler kontrol edildikten veya zaten verilmişse taramayı başlat
bluetoothLeScanner?.startScan(null, scanSettings, leScanCallback)
Toast.makeText(this, "BLE taraması başlatıldı...", Toast.LENGTH_SHORT).show()
// Belirli bir süre sonra taramayı durdurmak için Handler kullanın
Handler(Looper.getMainLooper()).postDelayed({ stopBleScan() }, SCAN_PERIOD)
}
fun stopBleScan() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S && (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED)) {
// İzinleri kontrol et, gerekirse tekrar iste (veya taramayı durdurma)
Log.w("BLE", "BLE taraması durdurulamadı, izinler eksik.")
return
}
bluetoothLeScanner?.stopScan(leScanCallback)
Toast.makeText(this, "BLE taraması durduruldu.", Toast.LENGTH_SHORT).show()
}
Bir BLE cihaza bağlandıktan sonra, BluetoothGatt objesini kullanarak servisleri keşfeder, karakteristikleri okur/yazar ve bildirimlere (notifications) abone olabilirsiniz. Bağlantı, BluetoothDevice objesinin connectGatt() metodu ile kurulur ve bir BluetoothGattCallback üzerinden bağlantı durumu, servis keşfi ve karakteristik değişiklikleri gibi olaylar dinlenir.
private var bluetoothGatt: BluetoothGatt? = null
private val gattCallback = object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt?, status: Int, newState: Int) {
super.onConnectionStateChange(gatt, status, newState)
if (newState == BluetoothProfile.STATE_CONNECTED) {
Log.i("BLE", "BLE cihazına bağlanıldı.")
gatt?.discoverServices() // Servisleri keşfet
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
Log.i("BLE", "BLE cihazından bağlantı kesildi.")
bluetoothGatt?.close()
bluetoothGatt = null
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt?, status: Int) {
super.onServicesDiscovered(gatt, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
gatt?.services?.forEach { service ->
Log.d("BLE", "Servis bulundu: ${service.uuid}")
service.characteristics.forEach { characteristic ->
Log.d("BLE", "Karakteristik bulundu: ${characteristic.uuid}")
// İstenilen karakteristikten veri okuma veya bildirimlere abone olma
if (characteristic.uuid == MY_CHARACTERISTIC_UUID) {
gatt.readCharacteristic(characteristic) // Tek seferlik okuma
gatt.setCharacteristicNotification(characteristic, true) // Bildirimleri etkinleştir
val descriptor = characteristic.getDescriptor(CCC_DESCRIPTOR_UUID)
descriptor?.value = BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE
gatt.writeDescriptor(descriptor) // Bildirimleri açmak için descriptor yaz
}
}
}
} else {
Log.w("BLE", "Servis keşfi başarısız oldu: $status")
}
}
override fun onCharacteristicRead(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
super.onCharacteristicRead(gatt, characteristic, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
val value = characteristic?.value?.let { String(it, Charsets.UTF_8) }
Log.d("BLE", "Karakteristik okundu: ${characteristic?.uuid} -> $value")
}
}
override fun onCharacteristicChanged(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, value: ByteArray?) {
super.onCharacteristicChanged(gatt, characteristic, value)
val changedValue = value?.let { String(it, Charsets.UTF_8) }
Log.d("BLE", "Karakteristik değişti (bildirim): ${characteristic?.uuid} -> $changedValue")
// UI'ı güncelle
}
override fun onCharacteristicWrite(gatt: BluetoothGatt?, characteristic: BluetoothGattCharacteristic?, status: Int) {
super.onCharacteristicWrite(gatt, characteristic, status)
if (status == BluetoothGatt.GATT_SUCCESS) {
Log.d("BLE", "Karakteristik başarıyla yazıldı: ${characteristic?.uuid}")
} else {
Log.e("BLE", "Karakteristik yazma başarısız oldu: $status")
}
}
}
fun connectToBleDevice(device: BluetoothDevice) {
if (bluetoothGatt == null) {
bluetoothGatt = device.connectGatt(this, false, gattCallback)
// AutoConnect false olarak ayarlanmıştır. Gerekirse true yapabilirsiniz.
// false: Cihaz bağlantıdan uzaklaşsa bile otomatik olarak tekrar bağlanmaya çalışmaz.
// true: Bağlantı kesildiğinde otomatik olarak tekrar bağlanmaya çalışır.
} else {
bluetoothGatt?.connect() // Zaten bağlıysa tekrar bağlanmayı dener
}
}
BLE ile çalışırken dikkat edilmesi gereken en önemli nokta, işlemlerin asenkron doğasıdır. BluetoothGattCallback üzerindeki tüm metodlar, Android'in ana iş parçacığında çağrılır, bu nedenle UI güncellemelerini doğrudan bu metodlar içinde yapabilirsiniz. Ancak, uzun süreli veya yoğun işlemler için yine de ayrı iş parçacıkları kullanmak akıllıca olacaktır. Ayrıca, pil ömrünü korumak adına tarama ve bağlantı sürelerini optimize etmek, uygulamanızın kullanıcılar tarafından tercih edilmesini sağlayacaktır. BLE, modern uygulamaların enerji verimliliği ve IoT entegrasyonu ihtiyaçları için vazgeçilmez bir araçtır.
BLE Bağlantısında Dikkat Edilmesi Gerekenler ve Optimizasyon
BLE, düşük güç tüketimi avantajları sunarken, geliştiricilerin dikkat etmesi gereken bazı özel durumlar ve optimizasyon teknikleri de mevcuttur. Bu konulara hakim olmak, hem uygulamanızın performansını artıracak hem de kullanıcı deneyimini iyileştirecektir.
Tarama Filtreleri: BLE taraması, çevresel cihazları keşfetmek için enerji tüketen bir işlemdir. Uygulamanızın sadece belirli cihazları veya servisleri aramasını sağlamak, pil ömrünü önemli ölçüde uzatabilir. ScanFilter objelerini kullanarak belirli cihaz adlarına, MAC adreslerine veya servis UUID'lerine göre filtreleme yapabilirsiniz:
val scanFilter = ScanFilter.Builder()
.setDeviceName("MyBLEDevice") // Belirli bir cihaz adını filtrele
// .setDeviceAddress("XX:XX:XX:XX:XX:XX") // Belirli bir MAC adresini filtrele
// .setServiceUuid(ParcelUuid(MY_SERVICE_UUID)) // Belirli bir servis UUID'sini filtrele
.build()
val filters: MutableList = mutableListOf()
filters.add(scanFilter)
bluetoothLeScanner?.startScan(filters, scanSettings, leScanCallback)
Bu filtreler, sadece ilgilendiğiniz cihazların onScanResult metodunuza ulaşmasını sağlar, böylece gereksiz işlem yükünü azaltır.
Bağlantı Parametreleri: BLE bağlantısının performansını ve güç tüketimini etkileyen önemli parametreler vardır: bağlantı aralığı (connection interval), gecikme (slave latency) ve denetim zaman aşımı (supervision timeout). BluetoothGatt.requestConnectionPriority() metodunu kullanarak bu parametreler üzerinde bir dereceye kadar kontrol sahibi olabilirsiniz. Örneğin, hızlı veri aktarımı gerektiğinde CONNECTION_PRIORITY_HIGH kullanmak gecikmeyi azaltabilir, ancak daha fazla güç tüketir. Daha az sıklıkta veri gereken durumlarda ise CONNECTION_PRIORITY_LOW_POWER tercih edilmelidir.
bluetoothGatt?.requestConnectionPriority(BluetoothGatt.CONNECTION_PRIORITY_HIGH)
Ancak unutulmamalıdır ki, bu sadece bir istektir ve bağlı cihaz ile Android sistemi bu isteği reddedebilir veya kendi parametrelerini uygulayabilir.
MTU (Maximum Transmission Unit) Yapılandırması: BLE'de varsayılan MTU boyutu genellikle 23 bayttır, bu da her pakette taşınabilecek veri miktarını sınırlar. Daha büyük veri paketleri göndermeniz gerektiğinde, MTU boyutunu artırmak, daha az paketle daha fazla veri göndermenizi sağlayarak verimliliği artırır. Bu, BluetoothGatt.requestMtu() metodu ile yapılır:
bluetoothGatt?.requestMtu(512) // Örneğin, MTU'yu 512 bayta ayarla
Bu isteğin sonucunu onMtuChanged() callback metodunda alırsınız. Her iki tarafın da MTU değişikliğini desteklemesi ve kabul etmesi gerektiğini unutmayın.
Hata Yönetimi ve Bağlantı Kesilmesi: BLE bağlantıları, özellikle mobil ortamlarda, çeşitli nedenlerle kesilebilir (cihazın menzil dışına çıkması, pil bitmesi vb.). onConnectionStateChange() metodunda STATE_DISCONNECTED durumunu doğru bir şekilde yönetmek ve uygulamanızın bağlantı kesildiğinde ne yapacağını belirlemek önemlidir. Otomatik tekrar bağlanma (connectGatt(..., true)) seçeneği bu durumlar için faydalı olabilir, ancak pil tüketimini artırabilir. Uygulamanızın hata durumlarında kullanıcıya geri bildirim sağlaması ve uygun kurtarma mekanizmalarını (örneğin, manuel tekrar bağlanma seçeneği) sunması önemlidir.
Arka Plan İşlemleri: Uygulamanızın arka planda BLE işlemleri yapması gerektiğinde (örneğin, sensör verilerini sürekli toplamak), Android'in arka plan işlem kısıtlamalarına dikkat etmelisiniz. Genellikle bir Foreground Service kullanmak, uygulamanızın arka planda Bluetooth tarama veya bağlantı işlemlerini güvenilir bir şekilde sürdürmesini sağlar ve kullanıcıya bir bildirim göstererek uygulamanın aktif olduğunu belirtir. Bu sayede, sistemin uygulamanızı öldürme olasılığı azalır ve kesintisiz BLE iletişimi sağlanır.
Bu optimizasyonlar ve dikkat edilecek noktalar, BLE tabanlı Android uygulamanızın daha kararlı, verimli ve kullanıcı dostu olmasına yardımcı olacaktır. Doğru stratejilerle, düşük enerji tüketimi ve yüksek performans arasında bir denge kurabilirsiniz.
Vaka Analizi: Akıllı Sensörden Veri Okuma
Bir akıllı sensörden (örneğin, bir sıcaklık sensörü) veri okuma, BLE'nin en yaygın ve etkili kullanım alanlarından biridir. Bu senaryoda, Android uygulamamız BLE özellikli sensöre bağlanacak ve periyodik olarak veya sensör bir değişiklik algıladığında sıcaklık verisini alacaktır.
Senaryo Akışı:
- Sensörün Taranması: Android uygulaması, öncelikle belirlenmiş bir servis UUID'sine (örneğin, Ortam Sıcaklığı Servisi'nin UUID'si) sahip BLE sensörlerini tarar. Bu, tarama filtreleri kullanılarak daha verimli hale getirilir.
- Bağlantı Kurulumu: Uygulama, algılanan sensörlerden birine
connectGatt() metodu ile bağlanır.
- Servis ve Karakteristik Keşfi: Bağlantı kurulduktan sonra, uygulama
discoverServices() çağrısı yaparak sensörün sunduğu tüm GATT servislerini ve karakteristiklerini keşfeder.
- Sıcaklık Karakteristiğini Bulma: Keşfedilen servisler arasında, sıcaklık verisini içeren spesifik karakteristik (örneğin, Sıcaklık Karakteristiği) bulunur.
- Veri Okuma veya Bildirimlere Abone Olma:
- Periyodik Okuma: Uygulama,
readCharacteristic() metodunu kullanarak belirli aralıklarla (örneğin, her 5 saniyede bir) sıcaklık değerini okuyabilir. Bu, sensörün veri güncelleme hızı düşük olduğunda veya sadece anlık bir okuma gerektiğinde kullanılır.
- Bildirimlere Abone Olma: Daha dinamik bir yaklaşım için, uygulama sıcaklık karakteristiği için bildirimlere (notifications) abone olabilir. Sensör, sıcaklık değeri değiştiğinde veya belirli bir aralıkta otomatik olarak uygulamaya veri gönderecektir. Bu,
setCharacteristicNotification(characteristic, true) ve ardından ilgili istemci karakteristik yapılandırma açıklayıcısına (Client Characteristic Configuration Descriptor - CCCD) yazma işlemiyle yapılır.
- Verilerin İşlenmesi ve Gösterimi: Alınan sıcaklık verisi (genellikle bayt dizisi olarak gelir), uygulamanın ihtiyacına göre dönüştürülür (örneğin, IEEE-11073 standardına göre float değerine çevrilir) ve kullanıcı arayüzünde gösterilir.
- Hata Yönetimi ve Bağlantı Kesilmesi: Bağlantı kesilmesi veya veri okuma hataları gibi durumlar, uygulamanın sağlamlığını artırmak için uygun şekilde ele alınır.
Bu vaka analizi, BLE'nin akıllı sensörlerden gerçek zamanlı veri toplama yeteneğini ve modern IoT uygulamalarının temelini nasıl oluşturduğunu göstermektedir. Uygulama, kullanıcılara anlık sıcaklık bilgisi sunarak veya uzun vadeli veri trendlerini takip ederek değerli bir deneyim sağlayabilir. Ayrıca, bu veriler bulut tabanlı bir platforma gönderilerek daha geniş çaplı analizler veya otomasyon senaryoları için de kullanılabilir.
Android Bluetooth Uygulamalarında Kullanıcı Deneyimi ve Mobil Uyumluluk
Bir Android uygulamasında Bluetooth işlevselliği sunmak, sadece teknik entegrasyonla sınırlı değildir; aynı zamanda mükemmel bir kullanıcı deneyimi (UX) ve farklı cihazlarda sorunsuz mobil uyumluluk sağlamak da büyük önem taşır. Kullanıcıların Bluetooth bağlantılarını kolayca yönetebilmesi, veri akışını anlayabilmesi ve uygulamanın çeşitli ekran boyutlarına ve yönelimlerine adaptasyonu, uygulamanın başarısını doğrudan etkiler.
Kullanıcı Dostu Arayüz Tasarımı ve Geri Bildirim
Bluetooth işlemleri genellikle arama, bağlanma, veri gönderme/alma gibi birden fazla adımı içerir ve bu adımlar zaman alıcı veya karmaşık olabilir. Bu nedenle, uygulamanızın arayüzünün sezgisel ve bilgilendirici olması gerekir:
- Net Durum Göstergeleri: Kullanıcıya Bluetooth'un açık mı, kapalı mı, tarama yapıyor mu, bağlı mı gibi durumlarını açıkça gösterin. Bağlantı durumundaki değişiklikleri anında yansıtın (örneğin, bir durum metni, renk değişimi).
- İlerleme Göstergeleri: Cihaz taraması, bağlantı kurma veya veri transferi gibi zaman alıcı işlemler sırasında bir yükleme animasyonu (spinner) veya ilerleme çubuğu göstererek kullanıcıya geri bildirim sağlayın. Bu, kullanıcının uygulamanın donduğunu düşünmesini engeller.
- Erişilebilir Hata Mesajları: Bağlantı hataları, izin reddi veya cihaz bulunamaması gibi durumlarda, kullanıcıya ne olduğunu açıklayan ve sorunu çözmek için adımlar öneren anlaşılır hata mesajları sunun. Toast mesajları veya AlertDialog'lar bu amaçla kullanılabilir.
- Basit Eylem Düğmeleri: Bluetooth'u açma, taramayı başlatma veya bir cihaza bağlanma gibi eylemler için belirgin ve anlaşılır düğmeler kullanın.
- Cihaz Listesi Yönetimi: Keşfedilen cihazları kullanıcı dostu bir liste (RecyclerView) içinde gösterin, cihaz adlarını ve mümkünse sinyal gücünü (RSSI) de dahil edin. Kullanıcıların kolayca seçim yapmasını sağlayın.
Arka Plan İşlemleri ve Bildirimler
Bluetooth uygulamaları genellikle ekran kapalıyken bile veri toplamaya veya bağlı kalmaya devam etmelidir. Android'in arka plan işlem kısıtlamaları (özellikle Doze modu ve App Standby) göz önüne alındığında, bu tür sürekli işlemler için Foreground Service kullanmak en iyi yaklaşımdır. Bir Foreground Service:
- Sisteme uygulamanızın aktif ve önemli bir görev yürüttüğünü bildirir.
- Kullanıcıya sürekli bir bildirim gösterir, bu da kullanıcının uygulamanın ne yaptığını bilmesini sağlar ve sistemin uygulamayı daha az öldürmesine neden olur.
- Bluetooth taraması ve bağlantısı gibi işlemleri daha güvenilir bir şekilde sürdürmeye olanak tanır.
// Örnek bir Foreground Service başlatma
val serviceIntent = Intent(this, MyBluetoothService::class.java)
ContextCompat.startForegroundService(this, serviceIntent)
// MyBluetoothService içinde:
override fun onCreate() {
super.onCreate()
val notification = createNotification() // Bir bildirim oluştur
startForeground(NOTIFICATION_ID, notification) // Servisi foreground yap
}
Bildirimler aynı zamanda, Bluetooth bağlantısının durumu (bağlandı/bağlantı kesildi) veya önemli veri alımı gibi kullanıcı için kritik bilgileri anında iletmek için de kullanılabilir.
Mobil Uyumluluk ve Duyarlı Tasarım (Media Queries)
Android cihazların çeşitliliği göz önüne alındığında, uygulamanızın farklı ekran boyutları, çözünürlükler ve cihaz yönelimlerinde iyi görünmesi ve çalışması esastır. Geliştirme ortamınızda XML layout dosyalarını kullanarak duyarlı tasarımlar oluşturmak temeldir. Ancak eğer uygulamanızın web tabanlı bir arayüzü varsa veya WebView içinde HTML/CSS kullanıyorsanız, media queries mobil uyumluluk için hayati öneme sahiptir.
/* styles.css içinde bir örnek media query */
.bluetooth-device-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 16px;
padding: 16px;
}
@media (max-width: 600px) {
/* Küçük ekranlar (telefonlar) için */
.bluetooth-device-list {
grid-template-columns: 1fr; /* Tek sütunlu liste */
padding: 8px;
}
.device-item {
flex-direction: column;
align-items: flex-start;
}
}
@media (min-width: 601px) and (max-width: 1024px) {
/* Orta boyutlu ekranlar (tabletler dikey) için */
.bluetooth-device-list {
grid-template-columns: repeat(2, 1fr); /* İki sütunlu liste */
}
}
@media (min-width: 1025px) {
/* Büyük ekranlar (tabletler yatay, Chromebook'lar) için */
.bluetooth-device-list {
grid-template-columns: repeat(3, 1fr); /* Üç sütunlu liste */
}
}
Bu CSS media query örnekleri, uygulamanızın listeleri, düğmeleri ve metin boyutlarını farklı ekran genişliklerine göre otomatik olarak ayarlamasını sağlar. Android geliştirme bağlamında, bu tür dinamik UI adaptasyonları genellikle ConstraintLayout, LinearLayout ve RecyclerView gibi yerel bileşenler ve farklı res/layout dizinleri (örneğin layout-w600dp veya layout-land) kullanılarak elde edilir. Ancak modern Android geliştirmenin bazı alanlarında (örneğin, Compose WebView veya custom WebViews) hala HTML/CSS tabanlı içerikler için bu tür yaklaşımlar gerekebilir.
Sonuç olarak, kullanıcı deneyimi ve mobil uyumluluk, bir Bluetooth uygulamasının benimsenmesi ve başarısı için teknik yetenekler kadar önemlidir. Bu alanlara yatırım yapmak, uygulamanızın geniş bir kullanıcı kitlesi tarafından sevilmesini ve tercih edilmesini sağlayacaktır.
Sonuç ve Sıkça Sorulan Sorular
Bu makalede, Android Bluetooth geliştirmenin temelden ileri düzeye kadar birçok yönünü detaylı bir şekilde inceledik. Klasik Bluetooth'un güçlü veri transferi yeteneklerinden, Bluetooth Düşük Enerji (BLE) teknolojisinin enerji verimli IoT entegrasyonlarına kadar geniş bir yelpazeyi kapsayan konulara değindik. İzin yönetimi, cihaz keşfi, güvenli bağlantı kurma ve veri transferi için adım adım kod örnekleri sunarak, okuyucuların bu konularda sağlam bir temel oluşturmasına yardımcı olmayı amaçladık. Ayrıca, dosya paylaşımı ve akıllı sensörden veri okuma gibi gerçek dünya senaryolarıyla Klasik ve BLE Bluetooth'un pratik uygulamalarını gösterdik. Uygulamanızın başarısı için kullanıcı deneyiminin (UX) ve mobil uyumluluğun ne denli kritik olduğunu da vurguladık. Umuyoruz ki bu kapsamlı rehber, Android Bluetooth geliştirme yolculuğunuzda size yol gösterir ve projelerinizi bir üst seviyeye taşımanız için ilham verir.
Sıkça Sorulan Sorular
-
Soru: Android'de Bluetooth izinlerini neden çalışma zamanında istemem gerekiyor?
Cevap: Android 6.0 (Marshmallow) ve sonraki sürümlerden itibaren, kullanıcı gizliliğini ve güvenliğini artırmak amacıyla, uygulamanız belirli hassas izinleri (örneğin konum veya Bluetooth ile ilgili bazı izinler) çalışma zamanında kullanıcıdan onay alarak istemek zorundadır. Kullanıcılar bu izinleri istedikleri zaman ayarlardan iptal edebilirler. Bu durum, uygulamaların kullanıcı onayını açıkça almasını ve sadece ihtiyaç duydukları izinleri kullanmasını sağlar.
-
Soru: Klasik Bluetooth ve BLE arasındaki temel farklar nelerdir?
Cevap: Temel farklar şunlardır:
- Güç Tüketimi: BLE, adından da anlaşılacağı gibi, Klasik Bluetooth'a göre çok daha az güç tüketir ve bu da onu pille çalışan IoT cihazları için ideal kılar.
- Veri Hızı: Klasik Bluetooth genellikle daha yüksek veri hızlarına ulaşabilir (örneğin ses akışı için), BLE ise daha küçük veri paketleri için optimize edilmiştir.
- Bağlantı Süresi: BLE, genellikle daha hızlı bağlantı kurar.
- Uygulama Alanları: Klasik Bluetooth daha çok ses aktarımı, dosya transferi gibi sürekli ve yüksek bant genişliği gerektiren durumlar için kullanılırken, BLE giyilebilir cihazlar, sensörler ve akıllı ev aksesuarları gibi anlık veya periyodik düşük veri transferi gerektiren durumlar için tercih edilir.
-
Soru: Bir BLE cihazına bağlanırken 'autoConnect' parametresi ne işe yarar?
Cevap: BluetoothDevice.connectGatt() metodundaki autoConnect parametresi, bağlantı kesildiğinde Android sisteminin otomatik olarak tekrar bağlanmayı denemesi gerektiğini belirtir. Eğer true olarak ayarlanırsa, cihaz menzil dışına çıksa bile sistem arka planda tekrar bağlanmayı dener. Bu, kullanıcının manuel olarak tekrar bağlantı kurma zahmetinden kurtarır ancak pil tüketimini artırabilir. Eğer false olarak ayarlanırsa, bağlantı kesildiğinde uygulamanın manuel olarak tekrar bağlanma mantığını tetiklemesi gerekir.
-
Soru: Bluetooth taraması neden konum izni gerektirir?
Cevap: Android 10 (API 29) ve öncesinde, Bluetooth taraması (özellikle BLE taraması), çevredeki Wi-Fi ağlarını taramaya benzer şekilde cihazın yaklaşık konumunu belirlemek için kullanılabilir. Bu nedenle, Google, kullanıcı gizliliğini korumak amacıyla Bluetooth taraması için konum iznini zorunlu kılmıştır. Ancak Android 12 (API 31) ve sonraki sürümlerde, eğer uygulamanız Bluetooth taramasından konum bilgisi çıkarmayacağını beyan ederse (neverForLocation flag'i ile), BLUETOOTH_SCAN izniyle konum izni gerekliliği ortadan kalkar.
-
Soru: Bluetooth uygulamamın arka planda güvenilir bir şekilde çalışmasını nasıl sağlarım?
Cevap: Uygulamanızın arka planda uzun süreli veya kritik Bluetooth işlemleri yapması gerekiyorsa, bir Foreground Service kullanmanız önerilir. Foreground Service ile sistem, uygulamanızın aktif ve önemli bir görev yürüttüğünü bilir, bu da uygulamanızın daha az kesintiye uğramasını ve sistem tarafından sonlandırılmamasını sağlar. Ayrıca, kullanıcıya sürekli bir bildirim gösterilir, bu da kullanıcının uygulamanın arka planda çalıştığından haberdar olmasını sağlar.