Untuk membuat koneksi antara dua perangkat, Anda harus menerapkan
mekanisme sisi server dan sisi klien karena satu perangkat harus membuka soket
server, dan perangkat lainnya harus memulai koneksi menggunakan alamat MAC
perangkat server. Perangkat server dan perangkat klien masing-masing mendapatkan BluetoothSocket
yang diperlukan
dengan cara yang berbeda. Server menerima informasi soket saat koneksi masuk diterima. Klien memberikan informasi soket ketika membuka saluran RFCOMM
ke server.
Server dan klien dianggap terhubung satu sama lain jika masing-masing memiliki BluetoothSocket
yang terhubung pada saluran RFCOMM yang sama. Pada tahap ini, setiap
perangkat dapat memperoleh aliran input dan output, serta transfer data dapat dimulai, yang
dibahas di bagian tentang mentransfer data
Bluetooth. Bagian ini
menjelaskan cara memulai koneksi antara dua perangkat.
Pastikan Anda memiliki izin Bluetooth yang sesuai dan menyiapkan aplikasi untuk Bluetooth sebelum mencoba menemukan perangkat Bluetooth.
Teknik koneksi
Salah satu teknik implementasi adalah dengan otomatis menyiapkan setiap perangkat sebagai server sehingga setiap perangkat memiliki soket server yang terbuka dan memproses koneksi. Dalam hal ini, salah satu perangkat dapat memulai koneksi dengan perangkat lainnya dan menjadi klien. Atau, satu perangkat dapat secara eksplisit menghosting koneksi dan membuka soket server sesuai permintaan, dan perangkat lainnya akan memulai koneksi.
Gambar 1. Dialog penyambungan Bluetooth.
Menghubungkan sebagai server
Jika Anda ingin menghubungkan dua perangkat, salah satunya harus bertindak sebagai server dengan menahan
BluetoothServerSocket
terbuka.
Tujuan dari soket server adalah untuk memproses permintaan koneksi yang masuk dan menyediakan BluetoothSocket
yang terhubung setelah permintaan diterima. Saat
BluetoothSocket
diperoleh dari BluetoothServerSocket
, BluetoothServerSocket
dapat—dan harus—dihapus, kecuali jika Anda ingin
perangkat menerima lebih banyak koneksi.
Untuk menyiapkan soket server dan menerima koneksi, selesaikan langkah-langkah berikut:
Dapatkan
BluetoothServerSocket
dengan memanggillistenUsingRfcommWithServiceRecord(String, UUID)
.String ini adalah nama yang dapat diidentifikasi layanan Anda, yang akan otomatis ditulis oleh sistem ke entri database Service Discovery Protocol (SDP) baru di perangkat. Nama ini tidak tentu dan bisa berupa nama aplikasi Anda. Universally Unique Identifier (UUID) juga disertakan dalam entri SDP dan membentuk dasar untuk perjanjian koneksi dengan perangkat klien. Artinya, saat klien mencoba untuk terhubung dengan perangkat ini, klien tersebut akan membawa UUID yang secara unik mengidentifikasi layanan yang ingin dihubungkan. UUID ini harus cocok agar koneksi dapat diterima.
UUID adalah format 128-bit terstandardisasi untuk ID string yang digunakan untuk mengidentifikasi informasi secara unik. UUID digunakan untuk mengidentifikasi informasi yang harus unik dalam sistem atau jaringan karena probabilitas UUID yang diulangi pada dasarnya adalah nol. Kode ini dihasilkan secara independen, tanpa menggunakan otoritas terpusat. Dalam hal ini, digunakan untuk mengidentifikasi layanan Bluetooth aplikasi Anda secara unik. Agar UUID dapat digunakan dengan aplikasi, Anda dapat menggunakan salah satu dari banyak generator
UUID
acak di web, lalu melakukan inisialisasi UUID denganfromString(String)
.Mulai proses permintaan koneksi dengan memanggil
accept()
.Ini adalah panggilan pemblokir. Metode akan ditampilkan saat koneksi telah diterima atau terjadi pengecualian. Koneksi hanya diterima jika perangkat jarak jauh telah mengirimkan permintaan koneksi berisi UUID yang cocok dengan yang terdaftar pada soket server pemroses. Jika berhasil,
accept()
akan menampilkanBluetoothSocket
yang terhubung.Kecuali Anda ingin menyetujui koneksi tambahan, panggil
close()
.Panggilan metode ini melepaskan soket server dan semua resource-nya, tetapi tidak menutup
BluetoothSocket
terhubung yang telah ditampilkan olehaccept()
. Tidak seperti TCP/IP, RFCOMM hanya mengizinkan satu klien terhubung per saluran pada satu waktu. Jadi, dalam banyak kasus, sebaiknya segera panggilclose()
padaBluetoothServerSocket
setelah menerima soket yang terhubung.
Karena panggilan accept()
adalah panggilan pemblokir, jangan jalankan di
UI thread aktivitas utama. Mengeksekusinya di thread lain akan memastikan aplikasi Anda
masih dapat merespons interaksi pengguna lainnya. Biasanya masuk akal untuk melakukan semua tugas
yang melibatkan BluetoothServerSocket
atau BluetoothSocket
di thread baru
yang dikelola oleh aplikasi Anda. Untuk membatalkan panggilan yang diblokir seperti accept()
, panggil close()
di BluetoothServerSocket
atau BluetoothSocket
dari thread lain. Perlu
diperhatikan bahwa semua metode pada BluetoothServerSocket
atau BluetoothSocket
aman untuk thread.
Contoh
Berikut adalah thread yang disederhanakan untuk komponen server yang menerima koneksi masuk:
Kotlin
private inner class AcceptThread : Thread() { private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) { bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID) } override fun run() { // Keep listening until exception occurs or a socket is returned. var shouldLoop = true while (shouldLoop) { val socket: BluetoothSocket? = try { mmServerSocket?.accept() } catch (e: IOException) { Log.e(TAG, "Socket's accept() method failed", e) shouldLoop = false null } socket?.also { manageMyConnectedSocket(it) mmServerSocket?.close() shouldLoop = false } } } // Closes the connect socket and causes the thread to finish. fun cancel() { try { mmServerSocket?.close() } catch (e: IOException) { Log.e(TAG, "Could not close the connect socket", e) } } }
Java
private class AcceptThread extends Thread { private final BluetoothServerSocket mmServerSocket; public AcceptThread() { // Use a temporary object that is later assigned to mmServerSocket // because mmServerSocket is final. BluetoothServerSocket tmp = null; try { // MY_UUID is the app's UUID string, also used by the client code. tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket's listen() method failed", e); } mmServerSocket = tmp; } public void run() { BluetoothSocket socket = null; // Keep listening until exception occurs or a socket is returned. while (true) { try { socket = mmServerSocket.accept(); } catch (IOException e) { Log.e(TAG, "Socket's accept() method failed", e); break; } if (socket != null) { // A connection was accepted. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(socket); mmServerSocket.close(); break; } } } // Closes the connect socket and causes the thread to finish. public void cancel() { try { mmServerSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the connect socket", e); } } }
Dalam contoh ini, hanya satu koneksi masuk yang diinginkan, jadi segera setelah
koneksi diterima dan BluetoothSocket
diperoleh, aplikasi akan meneruskan
BluetoothSocket
yang diperoleh ke thread terpisah, menutup
BluetoothServerSocket
, dan keluar dari loop.
Perhatikan bahwa saat accept()
menampilkan BluetoothSocket
, soket sudah
terhubung. Oleh karena itu, Anda tidak boleh memanggil
connect()
, seperti yang Anda lakukan
dari sisi klien.
Metode manageMyConnectedSocket()
khusus aplikasi dirancang guna memulai
thread untuk mentransfer data, yang akan dibahas dalam topik
mentransfer data
Bluetooth.
Biasanya, Anda harus segera menutup BluetoothServerSocket
setelah selesai memproses koneksi yang masuk. Dalam contoh ini, close()
dipanggil segera
setelah BluetoothSocket
diperoleh. Anda mungkin juga perlu menyediakan metode
publik di thread yang dapat menutup BluetoothSocket
pribadi jika
Anda perlu berhenti memproses soket server tersebut.
Menghubungkan sebagai klien
Untuk memulai koneksi dengan perangkat jarak jauh yang menerima
koneksi pada soket server terbuka, Anda harus terlebih dahulu mendapatkan objek BluetoothDevice
yang mewakili perangkat jarak jauh. Untuk mempelajari cara membuat
BluetoothDevice
, lihat Menemukan perangkat
Bluetooth. Kemudian, Anda harus
menggunakan BluetoothDevice
untuk memperoleh BluetoothSocket
dan memulai
koneksi.
Prosedur dasarnya sebagai berikut:
Dengan menggunakan
BluetoothDevice
, dapatkanBluetoothSocket
dengan memanggilcreateRfcommSocketToServiceRecord(UUID)
.Metode ini menginisialisasi objek
BluetoothSocket
yang memungkinkan klien terhubung keBluetoothDevice
. UUID yang diteruskan di sini harus cocok dengan UUID yang digunakan oleh perangkat server saat memanggillistenUsingRfcommWithServiceRecord(String, UUID)
untuk membukaBluetoothServerSocket
. Untuk menggunakan UUID yang cocok, lakukan hard code string UUID ke dalam aplikasi, lalu referensikan dari server dan kode klien.Mulai koneksi dengan memanggil
connect()
. Perhatikan, metode ini adalah panggilan pemblokir.Setelah klien memanggil metode ini, sistem akan melakukan pencarian SDP untuk menemukan perangkat jarak jauh dengan UUID yang cocok. Jika pencarian berhasil dan perangkat jarak jauh menerima koneksi, perangkat tersebut akan membagikan saluran RFCOMM untuk digunakan selama koneksi, dan metode
connect()
akan ditampilkan. Jika koneksi gagal, atau jika waktu metodeconnect()
habis (setelah sekitar 12 detik), metode tersebut akan menampilkanIOException
.
Karena connect()
adalah panggilan pemblokiran, Anda harus selalu melakukan
prosedur koneksi ini dalam thread yang terpisah dari thread aktivitas (UI)
utama.
Contoh
Berikut adalah contoh dasar thread klien yang memulai koneksi Bluetooth:
Kotlin
private inner class ConnectThread(device: BluetoothDevice) : Thread() { private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) { device.createRfcommSocketToServiceRecord(MY_UUID) } public override fun run() { // Cancel discovery because it otherwise slows down the connection. bluetoothAdapter?.cancelDiscovery() mmSocket?.let { socket -> // Connect to the remote device through the socket. This call blocks // until it succeeds or throws an exception. socket.connect() // The connection attempt succeeded. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(socket) } } // Closes the client socket and causes the thread to finish. fun cancel() { try { mmSocket?.close() } catch (e: IOException) { Log.e(TAG, "Could not close the client socket", e) } } }
Java
private class ConnectThread extends Thread { private final BluetoothSocket mmSocket; private final BluetoothDevice mmDevice; public ConnectThread(BluetoothDevice device) { // Use a temporary object that is later assigned to mmSocket // because mmSocket is final. BluetoothSocket tmp = null; mmDevice = device; try { // Get a BluetoothSocket to connect with the given BluetoothDevice. // MY_UUID is the app's UUID string, also used in the server code. tmp = device.createRfcommSocketToServiceRecord(MY_UUID); } catch (IOException e) { Log.e(TAG, "Socket's create() method failed", e); } mmSocket = tmp; } public void run() { // Cancel discovery because it otherwise slows down the connection. bluetoothAdapter.cancelDiscovery(); try { // Connect to the remote device through the socket. This call blocks // until it succeeds or throws an exception. mmSocket.connect(); } catch (IOException connectException) { // Unable to connect; close the socket and return. try { mmSocket.close(); } catch (IOException closeException) { Log.e(TAG, "Could not close the client socket", closeException); } return; } // The connection attempt succeeded. Perform work associated with // the connection in a separate thread. manageMyConnectedSocket(mmSocket); } // Closes the client socket and causes the thread to finish. public void cancel() { try { mmSocket.close(); } catch (IOException e) { Log.e(TAG, "Could not close the client socket", e); } } }
Perhatikan bahwa dalam cuplikan ini, cancelDiscovery()
dipanggil sebelum upaya
koneksi dilakukan. Anda harus selalu memanggil cancelDiscovery()
sebelum connect()
,
terutama karena cancelDiscovery()
berhasil, terlepas dari apakah penemuan
perangkat sedang berlangsung atau tidak. Jika aplikasi perlu menentukan apakah
penemuan perangkat sedang berlangsung, Anda dapat memeriksanya menggunakan
isDiscovering()
.
Metode manageMyConnectedSocket()
khusus aplikasi dirancang guna memulai
thread untuk mentransfer data, yang dibahas di bagian
mentransfer data Bluetooth.
Setelah selesai menggunakan BluetoothSocket
, selalu panggil close()
. Tindakan ini
akan segera menutup soket yang terhubung dan melepaskan semua resource internal
terkait.