Ringkasan Bluetooth

Platform Android menyertakan dukungan untuk stack jaringan Bluetooth, yang memungkinkan perangkat bertukar data secara nirkabel dengan perangkat Bluetooth lainnya. Framework aplikasi menyediakan akses ke fungsi Bluetooth melalui Android Bluetooth API. API ini memungkinkan aplikasi terhubung secara nirkabel ke perangkat Bluetooth lainnya, sehingga mengaktifkan fitur nirkabel point-to-point dan multipoint.

Menggunakan Bluetooth API, aplikasi Android dapat melakukan hal berikut:

  • Memindai perangkat Bluetooth lain
  • Melakukan kueri adaptor Bluetooth lokal untuk perangkat Bluetooth yang disandingkan
  • Membangun saluran RFCOMM
  • Terhubung ke perangkat lain melalui pencarian layanan
  • Mentransfer data ke dan dari perangkat lain
  • Mengelola beberapa koneksi

Halaman ini berfokus pada Bluetooth Klasik. Bluetooth Klasik adalah pilihan yang tepat untuk operasi yang menggunakan baterai lebih intensif, yang mencakup streaming dan komunikasi antar-perangkat Android. Untuk perangkat Bluetooth dengan persyaratan daya rendah, Android 4.3 (API level 18) memperkenalkan dukungan API untuk Bluetooth Hemat Energi. Untuk mempelajari lebih lanjut, lihat Bluetooth Hemat Energi.

Dokumen ini menjelaskan profil Bluetooth yang berbeda, termasuk Profil Perangkat Kesehatan. Dokumen ini kemudian menjelaskan cara menggunakan Android Bluetooth API untuk menyelesaikan empat tugas utama yang diperlukan untuk berkomunikasi menggunakan Bluetooth: menyiapkan Bluetooth, menemukan perangkat yang disambungkan atau tersedia di area lokal, menghubungkan perangkat, dan mentransfer data antar-perangkat.

Dasar-dasar

Agar perangkat berkemampuan Bluetooth dapat mengirimkan data satu sama lain, perangkat tersebut harus terlebih dahulu membentuk saluran komunikasi menggunakan proses penyambungan. Satu perangkat, perangkat yang dapat ditemukan, membuatnya tersedia untuk permintaan koneksi masuk. Perangkat lain menemukan perangkat yang dapat ditemukan menggunakan proses penemuan layanan. Setelah perangkat yang dapat ditemukan menerima permintaan penyambungan, kedua perangkat tersebut menyelesaikan proses ikatan tempat mereka bertukar kunci keamanan. Perangkat melakukan cache pada kunci ini untuk digunakan nanti. Setelah proses penyambungan dan ikatan selesai, kedua perangkat tersebut bertukar informasi. Setelah sesi selesai, perangkat yang memulai permintaan penyambungan akan merilis saluran yang telah menautkannya ke perangkat yang dapat ditemukan. Namun, kedua perangkat tersebut tetap terikat, sehingga dapat terhubung kembali secara otomatis selama sesi mendatang selama berada dalam jangkauan satu sama lain dan tidak ada perangkat yang melepaskan ikatannya.

Izin Bluetooth

Untuk menggunakan fitur Bluetooth di aplikasi, Anda harus mendeklarasikan dua izin. Yang pertama adalah BLUETOOTH. Anda memerlukan izin ini untuk melakukan komunikasi Bluetooth apa pun, seperti meminta koneksi, menerima koneksi, dan mentransfer data.

Izin lain yang harus Anda deklarasikan adalah ACCESS_FINE_LOCATION. Aplikasi memerlukan izin ini karena pemindaian Bluetooth dapat digunakan untuk mengumpulkan informasi tentang lokasi pengguna. Informasi ini dapat berasal dari perangkat pengguna sendiri, serta beacon Bluetooth yang digunakan di lokasi seperti toko dan fasilitas transportasi umum.

Layanan yang berjalan di Android 10 dan yang lebih tinggi tidak dapat menemukan perangkat Bluetooth kecuali jika memiliki izin ACCESS_BACKGROUND_LOCATION. Untuk mengetahui informasi selengkapnya tentang persyaratan ini, lihat Mengakses lokasi di latar belakang.

Cuplikan kode berikut menunjukkan cara memeriksa izin.

Kotlin

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    if (ContextCompat.checkSelfPermission(baseContext,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                    PERMISSION_CODE)
        }
}

Java

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  if (ContextCompat.checkSelfPermission(baseContext,
      Manifest.permission.ACCESS_BACKGROUND_LOCATION)
      != PackageManager.PERMISSION_GRANTED) {
          ActivityCompat.requestPermissions(
              MyActivity.this,
              new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                  PERMISSION_CODE)
      }
}

Pengecualian untuk persyaratan izin ini adalah saat aplikasi Anda diinstal di perangkat yang menjalankan Android 11 atau yang lebih tinggi dan telah menggunakan penyambungan perangkat pendamping untuk mengaitkan perangkat. Dalam hal ini, setelah perangkat dikaitkan, aplikasi dapat memindai perangkat Bluetooth yang terkait tanpa memerlukan izin akses lokasi.

Pada perangkat yang menjalankan Android 8.0 (API level 26) dan yang lebih tinggi, Anda dapat menggunakan CompanionDeviceManager untuk melakukan pemindaian perangkat pendamping di sekitar atas nama aplikasi Anda tanpa memerlukan izin lokasi. Untuk informasi selengkapnya tentang opsi ini, lihat Penyambungan perangkat pendamping.

Catatan: Jika aplikasi Anda menargetkan Android 9 (API level 28) atau yang lebih rendah, Anda dapat mendeklarasikan izin ACCESS_COARSE_LOCATION, bukan izin ACCESS_FINE_LOCATION.

Jika ingin aplikasi memulai penemuan perangkat atau memanipulasi setelan Bluetooth, Anda harus mendeklarasikan izin BLUETOOTH_ADMIN selain izin BLUETOOTH. Sebagian besar aplikasi memerlukan izin ini agar dapat menemukan perangkat Bluetooth lokal. Kemampuan lain yang diberikan oleh izin ini tidak boleh digunakan, kecuali jika aplikasi tersebut adalah "power manager" yang mengubah setelan Bluetooth atas permintaan pengguna.

Mendeklarasikan izin Bluetooth dalam file manifes aplikasi Anda. Contoh:

<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

  <!-- If your app targets Android 9 or lower, you can declare
       ACCESS_COARSE_LOCATION instead. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  ...
</manifest>

Lihat referensi <uses-permission> untuk informasi selengkapnya tentang mendeklarasikan izin aplikasi.

Berkeja dengan profil

Mulai dari Android 3.0, Bluetooth API menyertakan dukungan untuk menggunakan profil Bluetooth. Profil Bluetooth adalah spesifikasi antarmuka nirkabel untuk komunikasi berbasis Bluetooth antarperangkat. Contohnya adalah profil Hands-Free. Agar ponsel dapat terhubung ke headset nirkabel, kedua perangkat harus mendukung profil Hands-Free.

Android Bluetooth API menyediakan implementasi untuk profil Bluetooth berikut:

  • Headset. Profil Headset menyediakan dukungan untuk headset Bluetooth yang akan digunakan dengan ponsel. Android menyediakan class BluetoothHeadset, yang merupakan proxy untuk mengontrol Layanan Headset Bluetooth. Hal ini mencakup profil Bluetooth Headset dan Hands-Free (v1.5). Class BluetoothHeadset menyertakan dukungan untuk perintah AT. Untuk pembahasan lebih lanjut tentang topik ini, lihat Perintah AT khusus vendor
  • A2DP. Profil Advanced Audio Distribution Profile (A2DP) menentukan cara audio berkualitas tinggi dapat di-streaming dari satu perangkat ke perangkat lainnya melalui koneksi Bluetooth. Android menyediakan class BluetoothA2dp, yang merupakan proxy untuk mengontrol Layanan Bluetooth A2DP.
  • Perangkat Kesehatan. Android 4.0 (API level 14) memperkenalkan dukungan untuk Bluetooth Health Device Profile (HDP). Hal ini memungkinkan Anda membuat aplikasi yang menggunakan Bluetooth untuk berkomunikasi dengan perangkat kesehatan yang mendukung Bluetooth, seperti pemantau detak jantung, pengukur darah, termometer, timbangan, dan lainnya. Untuk daftar perangkat yang didukung dan kode spesialisasi data perangkat yang sesuai, lihat Spesialisasi Data Perangkat HDP Bluetooth. Nilai-nilai ini juga dirujuk dalam spesifikasi ISO/IEEE 11073-20601 [7] sebagai MDC_DEV_SPEC_PROFILE_* dalam Lampiran Kode Nomenklatur. Untuk diskusi selengkapnya tentang HDP, lihat Profil Perangkat Kesehatan.

Berikut adalah langkah-langkah dasar untuk bekerja dengan profil:

  1. Dapatkan adaptor default, seperti yang dijelaskan dalam Menyiapkan Bluetooth.
  2. Siapkan BluetoothProfile.ServiceListener. Pemroses ini memberi tahu klien BluetoothProfile saat mereka terhubung ke atau terputus dari layanan.
  3. Gunakan getProfileProxy() untuk menghubungkan ke objek proxy profil yang terkait dengan profil. Pada contoh di bawah, objek proxy profil adalah instance BluetoothHeadset.
  4. Di onServiceConnected(), dapatkan handle untuk objek proxy profil.
  5. Setelah memiliki objek proxy profil, Anda dapat menggunakannya untuk memantau status koneksi dan melakukan operasi lain yang relevan dengan profil tersebut.

Misalnya, cuplikan kode ini menunjukkan cara menghubungkan ke objek proxy BluetoothHeadset sehingga Anda dapat mengontrol profil Headset:

Kotlin

var bluetoothHeadset: BluetoothHeadset? = null

// Get the default adapter
val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()

private val profileListener = object : BluetoothProfile.ServiceListener {

    override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = proxy as BluetoothHeadset
        }
    }

    override fun onServiceDisconnected(profile: Int) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = null
        }
    }
}

// Establish connection to the proxy.
bluetoothAdapter?.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET)

// ... call functions on bluetoothHeadset

// Close proxy connection after use.
bluetoothAdapter?.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset)

Java

BluetoothHeadset bluetoothHeadset;

// Get the default adapter
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

private BluetoothProfile.ServiceListener profileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = null;
        }
    }
};

// Establish connection to the proxy.
bluetoothAdapter.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET);

// ... call functions on bluetoothHeadset

// Close proxy connection after use.
bluetoothAdapter.closeProfileProxy(bluetoothHeadset);

Perintah AT khusus-vendor

Mulai Android 3.0 (API level 11), aplikasi dapat mendaftar untuk menerima siaran sistem dari perintah AT khusus vendor yang telah ditetapkan dan dikirim oleh headset (seperti perintah Plantronics +XEVENT). Misalnya, aplikasi dapat menerima siaran yang menunjukkan level baterai perangkat yang terhubung dan dapat memberi tahu pengguna atau melakukan tindakan lain sesuai kebutuhan. Buat penerima siaran untuk intent ACTION_VENDOR_SPECIFIC_HEADSET_EVENT guna menangani perintah AT khusus vendor untuk headset.

Profil perangkat kesehatan

Android 4.0 (API level 14) memperkenalkan dukungan untuk Bluetooth Health Device Profile (HDP). Hal ini memungkinkan Anda membuat aplikasi yang menggunakan Bluetooth untuk berkomunikasi dengan perangkat kesehatan yang mendukung Bluetooth, seperti pemantau detak jantung, meter darah, termometer, dan timbangan. Bluetooth Health API menyertakan class BluetoothHealth, BluetoothHealthCallback, dan BluetoothHealthAppConfiguration, yang dijelaskan dalam Antarmuka dan Class Utama.

Dalam menggunakan Bluetooth Health API, akan sangat membantu untuk memahami konsep-konsep penting HDP berikut:

Sumber
Perangkat kesehatan—seperti timbangan, pengukur glukosa, atau termometer—yang mengirimkan data medis ke perangkat smart, seperti ponsel atau tablet Android.
Wastafel
Perangkat smart yang menerima data medis. Dalam aplikasi HDP Android, sink diwakili oleh objek BluetoothHealthAppConfiguration.
Pendaftaran
Proses yang digunakan untuk mendaftarkan sink untuk berkomunikasi dengan perangkat kesehatan tertentu.
Koneksi
Proses yang digunakan untuk membuka saluran antara perangkat kesehatan (sumber) dan perangkat smart (sink).

Membuat aplikasi HDP

Berikut adalah langkah-langkah dasar yang harus dilakukan untuk membuat aplikasi HDP Android:

  1. Dapatkan referensi ke objek proxy BluetoothHealth.

    Serupa dengan headset biasa dan perangkat profil A2DP, Anda harus memanggil getProfileProxy() dengan BluetoothProfile.ServiceListener dan jenis profil HEALTH untuk membuat koneksi dengan objek proxy profil.

  2. Buat BluetoothHealthCallback dan daftarkan konfigurasi aplikasi (BluetoothHealthAppConfiguration) yang bertindak sebagai sink kesehatan.
  3. Membangun koneksi ke perangkat kesehatan.

    Catatan: Beberapa perangkat memulai koneksi secara otomatis. Tidak diperlukan menjalankan langkah ini untuk perangkat tersebut.

  4. Ketika berhasil terhubung ke perangkat kesehatan, baca/tulis ke perangkat kesehatan menggunakan deskriptor file. Data yang diterima perlu ditafsirkan menggunakan pengelola kesehatan yang menerapkan spesifikasi IEEE 11073.
  5. Setelah selesai, tutup saluran kesehatan dan cabut pendaftaran aplikasi. Saluran juga ditutup ketika tidak ada aktivitas dalam waktu lama.

Menyiapkan bluetooth

Sebelum aplikasi dapat berkomunikasi melalui Bluetooth, Anda harus memverifikasi bahwa Bluetooth didukung pada perangkat, dan jika ya, pastikan bahwa aplikasi diaktifkan.

Jika Bluetooth tidak didukung, Anda harus menonaktifkan semua fitur Bluetooth dengan baik. Jika Bluetooth didukung, tetapi dinonaktifkan, Anda dapat meminta pengguna mengaktifkan Bluetooth tanpa keluar dari aplikasi. Penyiapan ini dilakukan dalam dua langkah, menggunakan BluetoothAdapter:

  1. Dapatkan BluetoothAdapter.

    BluetoothAdapter diperlukan untuk setiap dan semua aktivitas Bluetooth. Untuk mendapatkan BluetoothAdapter, panggil metode getDefaultAdapter() statis. Tindakan ini akan menampilkan BluetoothAdapter yang merepresentasikan adaptor Bluetooth milik perangkat (radio Bluetooth). Ada satu adaptor Bluetooth untuk seluruh sistem, dan aplikasi Anda dapat berinteraksi dengannya menggunakan objek ini. Jika getDefaultAdapter() menampilkan null, berarti perangkat tidak mendukung Bluetooth. Contoh:

    Kotlin

    val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
    if (bluetoothAdapter == null) {
        // Device doesn't support Bluetooth
    }
    

    Java

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        // Device doesn't support Bluetooth
    }
    
  2. Aktifkan Bluetooth.

    Berikutnya, Anda harus memastikan bahwa Bluetooth diaktifkan. Panggil isEnabled() untuk memeriksa apakah Bluetooth saat ini diaktifkan. Jika metode ini kembali ke false, maka Bluetooth dinonaktifkan. Untuk meminta agar Bluetooth diaktifkan, panggil startActivityForResult(), dengan meneruskan tindakan intent ACTION_REQUEST_ENABLE. Panggilan ini akan mengeluarkan permintaan untuk mengaktifkan Bluetooth melalui setelan sistem (tanpa menghentikan aplikasi Anda). Contoh:

    Kotlin

    if (bluetoothAdapter?.isEnabled == false) {
        val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
    }
    

    Java

    if (!bluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    

    Akan muncul dialog yang meminta izin pengguna untuk mengaktifkan Bluetooth, seperti yang ditunjukkan pada Gambar 1. Jika pengguna merespons "Yes", sistem akan mulai mengaktifkan Bluetooth, dan fokus akan kembali ke aplikasi setelah proses selesai (atau gagal).

    Gambar 1: Mengaktifkan dialog Bluetooth.

    Konstanta REQUEST_ENABLE_BT yang diteruskan ke startActivityForResult() adalah bilangan bulat yang ditentukan secara lokal yang harus lebih besar dari 0. Sistem meneruskan konstanta ini kembali kepada Anda dalam implementasi onActivityResult() sebagai parameter requestCode.

    Jika berhasil mengaktifkan Bluetooth, aktivitas Anda akan menerima kode hasil RESULT_OK di callback onActivityResult(). Jika Bluetooth tidak diaktifkan karena terjadi error (atau pengguna merespons "Tidak") maka kode hasilnya adalah RESULT_CANCELED.

Secara opsional, aplikasi Anda juga dapat memproses intent siaran ACTION_STATE_CHANGED, yang disiarkan sistem setiap kali status Bluetooth berubah. Siaran ini berisi kolom tambahan EXTRA_STATE dan EXTRA_PREVIOUS_STATE, yang masing-masing berisi status Bluetooth baru dan lama. Nilai yang mungkin untuk kolom tambahan ini adalah STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF, dan STATE_OFF. Mendengarkan siaran ini dapat berguna jika aplikasi Anda perlu mendeteksi perubahan runtime yang dilakukan pada status Bluetooth.

Tips: Mengaktifkan visibilitas akan otomatis mengaktifkan Bluetooth. Jika Anda berencana untuk selalu mengaktifkan visibilitas perangkat sebelum melakukan aktivitas Bluetooth, Anda dapat melewati langkah 2 di atas. Untuk mengetahui informasi selengkapnya, baca bagian mengaktifkan visibilitas di halaman ini.

Mencari perangkat

Dengan menggunakan BluetoothAdapter, Anda dapat menemukan perangkat Bluetooth jarak jauh melalui penemuan perangkat atau dengan membuat kueri daftar perangkat yang disambungkan.

Penemuan perangkat adalah prosedur pemindaian yang menelusuri area lokal untuk perangkat berkemampuan Bluetooth dan meminta beberapa informasi tentang setiap perangkat. Proses ini terkadang disebut sebagai menemukan, menanyakan, atau memindai. Namun, perangkat Bluetooth di sekitar akan merespons permintaan penemuan hanya jika perangkat tersebut saat ini menerima permintaan informasi dengan menjadi dapat ditemukan. Jika dapat ditemukan, perangkat akan merespons permintaan penemuan dengan membagikan beberapa informasi, seperti nama perangkat, class, dan alamat MAC uniknya. Dengan menggunakan informasi ini, perangkat yang melakukan proses penemuan dapat memilih untuk memulai koneksi ke perangkat yang ditemukan.

Karena perangkat yang dapat ditemukan dapat mengungkapkan informasi tentang lokasi pengguna, proses penemuan perangkat memerlukan akses lokasi. Jika aplikasi Anda digunakan pada perangkat yang menjalankan Android 8.0 (API level 26) atau yang lebih baru, gunakan Companion Device Manager API. API ini melakukan penemuan perangkat atas nama aplikasi Anda, sehingga aplikasi tidak perlu meminta izin akses lokasi.

Setelah koneksi dibuat dengan perangkat jarak jauh untuk pertama kalinya, permintaan penyambungan akan otomatis ditampilkan kepada pengguna. Jika perangkat disambungkan, informasi dasar tentang perangkat tersebut—seperti nama, class, dan alamat MAC perangkat—akan disimpan dan dapat dibaca menggunakan Bluetooth API. Dengan menggunakan alamat MAC yang dikenal untuk perangkat jarak jauh, koneksi dapat dimulai dengan perangkat tersebut kapan saja tanpa melakukan penemuan, dengan asumsi bahwa perangkat masih dalam jangkauan.

Ingat bahwa ada perbedaan antara yang disambungkan dan yang dihubungkan:

  • Disambungkan berarti dua perangkat saling mengetahui keberadaan masing-masing, memiliki kunci link bersama yang dapat digunakan untuk autentikasi, dan dapat saling membuat koneksi terenkripsi.
  • Terhubung berarti perangkat saat ini berbagi saluran RFCOMM dan dapat saling mengirim data. Android Bluetooth API saat ini mengharuskan perangkat untuk disambungkan sebelum koneksi RFCOMM dapat dibuat. Penyambungan secara otomatis dilakukan saat Anda memulai koneksi terenkripsi dengan Bluetooth API.

Bagian berikut menjelaskan cara menemukan perangkat yang telah disambungkan, atau menemukan perangkat baru menggunakan penemuan perangkat.

Catatan: Perangkat Android tidak dapat ditemukan secara default. Pengguna bisa membuat perangkat dapat ditemukan untuk waktu terbatas melalui setelan sistem, atau aplikasi dapat meminta pengguna mengaktifkan visibilitas tanpa keluar dari aplikasi. Untuk mengetahui informasi selengkapnya, lihat bagian mengaktifkan visibilitas di halaman ini.

Melakukan kueri perangkat yang disambungkan

Sebelum melakukan penemuan perangkat, ada baiknya membuat kueri kumpulan perangkat yang disambungkan untuk melihat apakah perangkat yang diinginkan sudah diketahui. Untuk melakukannya, panggil getBondedDevices(). Tindakan ini menampilkan kumpulan objek BluetoothDevice yang mewakili perangkat yang disambungkan. Misalnya, Anda dapat mengkueri semua perangkat yang disambungkan dan mendapatkan nama serta alamat MAC setiap perangkat, seperti yang ditunjukkan cuplikan kode berikut ini:

Kotlin

val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
    val deviceName = device.name
    val deviceHardwareAddress = device.address // MAC address
}

Java

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
    // There are paired devices. Get the name and address of each paired device.
    for (BluetoothDevice device : pairedDevices) {
        String deviceName = device.getName();
        String deviceHardwareAddress = device.getAddress(); // MAC address
    }
}

Untuk memulai koneksi dengan perangkat Bluetooth, yang diperlukan dari objek BluetoothDevice terkait adalah alamat MAC, yang Anda ambil dengan memanggil getAddress(). Anda dapat mempelajari lebih lanjut cara membuat koneksi di bagian Menghubungkan Perangkat.

Perhatian: Melakukan penemuan perangkat akan menghabiskan banyak resource adaptor Bluetooth. Setelah menemukan perangkat yang akan dihubungkan, pastikan Anda menghentikan penemuan dengan cancelDiscovery() sebelum mencoba menghubungkan. Selain itu, Anda tidak boleh melakukan penemuan saat terhubung ke perangkat karena proses penemuan secara signifikan mengurangi bandwidth yang tersedia untuk koneksi yang ada.

Menemukan perangkat

Untuk mulai menemukan perangkat, cukup panggil startDiscovery(). Proses ini bersifat asinkron dan menampilkan nilai boolean yang menunjukkan apakah penemuan telah berhasil dimulai. Proses penemuan biasanya melibatkan pemindaian pemeriksaan sekitar 12 detik, diikuti dengan pemindaian halaman dari setiap perangkat yang ditemukan untuk mengambil nama Bluetooth-nya.

Untuk menerima informasi tentang setiap perangkat yang ditemukan, aplikasi Anda harus mendaftarkan BroadcastReceiver untuk intent ACTION_FOUND. Sistem menyiarkan intent ini untuk setiap perangkat. Intent berisi kolom tambahan EXTRA_DEVICE dan EXTRA_CLASS, yang masing-masing berisi BluetoothDevice dan BluetoothClass. Cuplikan kode berikut menunjukkan cara mendaftar untuk menangani siaran saat perangkat ditemukan:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    // Register for broadcasts when a device is discovered.
    val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
    registerReceiver(receiver, filter)
}

// Create a BroadcastReceiver for ACTION_FOUND.
private val receiver = object : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val action: String = intent.action
        when(action) {
            BluetoothDevice.ACTION_FOUND -> {
                // Discovery has found a device. Get the BluetoothDevice
                // object and its info from the Intent.
                val device: BluetoothDevice =
                        intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                val deviceName = device.name
                val deviceHardwareAddress = device.address // MAC address
            }
        }
    }
}

override fun onDestroy() {
    super.onDestroy()
    ...

    // Don't forget to unregister the ACTION_FOUND receiver.
    unregisterReceiver(receiver)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    // Register for broadcasts when a device is discovered.
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    registerReceiver(receiver, filter);
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Discovery has found a device. Get the BluetoothDevice
            // object and its info from the Intent.
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            String deviceName = device.getName();
            String deviceHardwareAddress = device.getAddress(); // MAC address
        }
    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    ...

    // Don't forget to unregister the ACTION_FOUND receiver.
    unregisterReceiver(receiver);
}

Untuk memulai koneksi dengan perangkat Bluetooth, yang diperlukan dari objek BluetoothDevice terkait adalah alamat MAC, yang Anda ambil dengan memanggil getAddress(). Anda dapat mempelajari lebih lanjut cara membuat koneksi di bagian Menghubungkan Perangkat.

Perhatian: Melakukan penemuan perangkat akan menghabiskan banyak resource adaptor Bluetooth. Setelah menemukan perangkat yang akan dihubungkan, pastikan Anda menghentikan penemuan dengan cancelDiscovery() sebelum mencoba menghubungkan. Selain itu, Anda tidak boleh melakukan penemuan saat terhubung ke perangkat karena proses penemuan secara signifikan mengurangi bandwidth yang tersedia untuk koneksi yang ada.

Aktifkan visibilitas

Jika Anda ingin perangkat lokal dapat ditemukan oleh perangkat lain, panggil startActivityForResult(Intent, int) dengan intent ACTION_REQUEST_DISCOVERABLE. Tindakan ini mengeluarkan permintaan untuk mengaktifkan mode dapat ditemukan sistem tanpa harus membuka aplikasi Setelan, yang akan menghentikan aplikasi Anda. Secara default, perangkat dapat ditemukan selama 120 detik, atau 2 menit. Anda dapat menentukan durasi yang berbeda, hingga 3.600 detik (1 jam), dengan menambahkan tambahan EXTRA_DISCOVERABLE_DURATION.

Perhatian: Jika Anda menetapkan nilai tambahan EXTRA_DISCOVERABLE_DURATION ke 0, perangkat akan selalu dapat ditemukan. Konfigurasi ini tidak aman, sehingga sangat tidak dianjurkan.

Cuplikan kode berikut menyetel perangkat agar dapat ditemukan selama 5 menit (300 detik):

Kotlin

val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
    putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivity(discoverableIntent)

Java

Intent discoverableIntent =
        new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
Gambar 2: Dialog mengaktifkan visibilitas.

Dialog akan ditampilkan, yang meminta izin pengguna agar perangkat dapat ditemukan, seperti yang ditunjukkan pada Gambar 2. Jika pengguna merespons "Ya", perangkat akan dapat ditemukan selama jangka waktu tertentu. Aktivitas Anda kemudian menerima panggilan ke callback onActivityResult(), dengan kode hasil yang sama dengan durasi perangkat dapat ditemukan. Jika pengguna merespons "Tidak", atau jika terjadi error, kode hasil adalah RESULT_CANCELED.

Catatan: Jika Bluetooth belum diaktifkan di perangkat, membuat perangkat dapat ditemukan akan otomatis mengaktifkan Bluetooth.

Perangkat tersebut diam-diam akan tetap dalam mode dapat ditemukan untuk waktu yang telah ditentukan. Jika ingin menerima notifikasi saat mode dapat ditemukan berubah, Anda dapat mendaftarkan BroadcastReceiver untuk intent ACTION_SCAN_MODE_CHANGED. Intent ini berisi kolom tambahan EXTRA_SCAN_MODE dan EXTRA_PREVIOUS_SCAN_MODE, yang masing-masing menyediakan mode pemindaian baru dan lama. Nilai-nilai yang memungkinkan untuk setiap tambahan sebagai berikut:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
Perangkat dalam mode dapat ditemukan.
SCAN_MODE_CONNECTABLE
Perangkat tidak dalam mode dapat ditemukan, tetapi masih dapat menerima koneksi.
SCAN_MODE_NONE
Perangkat tidak dalam mode dapat ditemukan dan tidak dapat menerima koneksi.

Jika Anda memulai koneksi ke perangkat jarak jauh, Anda tidak perlu mengaktifkan visibilitas perangkat. Mengaktifkan visibilitas hanya diperlukan jika Anda ingin aplikasi menghosting soket server yang menerima koneksi masuk, karena perangkat jarak jauh harus dapat menemukan perangkat lain sebelum memulai koneksi ke perangkat lain tersebut.

Menghubungkan perangkat

Untuk membuat koneksi antara dua perangkat, Anda harus mengimplementasikan mekanisme sisi server dan sisi klien karena salah 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 saat membuka saluran RFCOMM ke server.

Server dan klien dianggap terhubung satu sama lain jika masing-masing memiliki BluetoothSocket yang terhubung di saluran RFCOMM yang sama. Pada tahap ini, setiap perangkat dapat memperoleh aliran input dan output, serta transfer data dapat dimulai, yang dibahas di bagian Mengelola koneksi. Bagian ini menjelaskan cara memulai koneksi antara dua perangkat.

Teknik koneksi

Salah satu teknik implementasi adalah secara 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 3: Dialog penyambungan Bluetooth.

Catatan: Jika kedua perangkat belum disambungkan sebelumnya, framework Android akan otomatis menampilkan notifikasi atau dialog permintaan penyambungan kepada pengguna selama prosedur koneksi, seperti yang ditunjukkan pada Gambar 3. Oleh karena itu, saat aplikasi Anda mencoba menghubungkan perangkat, aplikasi tidak perlu mengkhawatirkan apakah perangkat telah disambungkan atau tidak. Upaya koneksi RFCOMM Anda diblokir hingga pengguna berhasil menyambungkan kedua perangkat, dan upaya akan gagal jika pengguna menolak penyambungan, atau jika proses penyambungan gagal atau habis waktunya.

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:

  1. Dapatkan BluetoothServerSocket dengan memanggil listenUsingRfcommWithServiceRecord().

    String ini adalah nama layanan yang dapat diidentifikasi, yang akan otomatis ditulis oleh sistem ke entri database Service Discovery Protocol (SDP) baru di perangkat. Namanya bebas dan dapat berupa nama aplikasi. ID Unik Universal (UUID) juga disertakan dalam entri SDP dan membentuk dasar untuk perjanjian koneksi dengan perangkat klien. Artinya, saat klien mencoba 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. Inti dari UUID adalah bahwa itu cukup besar sehingga Anda bisa memilih ID acak dan tidak berbenturan dengan ID lain. 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 dengan fromString(String).

  2. 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 yang berisi UUID yang cocok dengan yang terdaftar dengan soket server pemroses. Jika berhasil, accept() akan menampilkan BluetoothSocket yang terhubung.

  3. Kecuali Anda ingin menerima koneksi tambahan, panggil close().

    Panggilan metode ini melepaskan soket server dan semua resource-nya, tetapi tidak menutup BluetoothSocket terhubung yang telah ditampilkan oleh accept(). Tidak seperti TCP/IP, RFCOMM hanya mengizinkan satu klien yang terhubung per saluran pada satu waktu. Jadi, dalam banyak kasus, sebaiknya panggil close() pada BluetoothServerSocket segera setelah menerima soket yang terhubung.

Karena panggilan accept() adalah panggilan pemblokiran, panggilan tersebut tidak boleh dieksekusi di UI thread aktivitas utama sehingga aplikasi Anda masih dapat merespons interaksi pengguna lainnya. Hal yang wajar untuk melakukan semua pekerjaan 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. Perhatikan 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 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 untuk memulai thread untuk mentransfer data, yang dibahas di bagian Mengelola Koneksi.

Biasanya, Anda harus menutup BluetoothServerSocket segera setelah Anda selesai memproses koneksi 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. Anda kemudian harus menggunakan BluetoothDevice untuk memperoleh BluetoothSocket dan memulai koneksi.

Prosedur dasarnya sebagai berikut:

  1. Menggunakan BluetoothDevice, dapatkan BluetoothSocket dengan memanggil createRfcommSocketToServiceRecord(UUID).

    Metode ini menginisialisasi objek BluetoothSocket yang memungkinkan klien terhubung ke BluetoothDevice. UUID yang diteruskan di sini harus cocok dengan UUID yang digunakan oleh perangkat server saat memanggil listenUsingRfcommWithServiceRecord(String, UUID) untuk membuka BluetoothServerSocket. Untuk menggunakan UUID yang cocok, lakukan hard code string UUID ke aplikasi Anda, lalu referensikan dari server dan kode klien.

  2. Mulai koneksi dengan memanggil connect(). Perlu diingat bahwa 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 metode connect() habis (setelah sekitar 12 detik), metode akan menampilkan IOException.

    Karena connect() adalah panggilan pemblokiran, Anda harus selalu melakukan prosedur koneksi ini dalam thread yang terpisah dari thread aktivitas (UI) utama.

    Catatan: Anda harus selalu memanggil cancelDiscovery() untuk memastikan perangkat tidak melakukan penemuan perangkat sebelum memanggil connect(). Jika penemuan sedang berlangsung, upaya koneksi akan melambat secara signifikan, dan kemungkinan besar akan gagal.

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?.use { 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. Namun, jika aplikasi Anda perlu menentukan apakah penemuan perangkat sedang berlangsung, Anda dapat memeriksanya menggunakan isDiscovering().

Metode manageMyConnectedSocket() khusus aplikasi dirancang untuk memulai thread untuk mentransfer data, yang dibahas di bagian Mengelola Koneksi.

Setelah selesai menggunakan BluetoothSocket, selalu panggil close(). Tindakan ini akan segera menutup soket yang terhubung dan melepaskan semua resource internal terkait.

Mengelola koneksi

Setelah Anda berhasil menghubungkan beberapa perangkat, setiap perangkat memiliki BluetoothSocket yang terhubung. Di sinilah kesenangan dimulai karena Anda dapat berbagi informasi antar perangkat. Dengan BluetoothSocket, prosedur umum untuk mentransfer data adalah sebagai berikut:

  1. Dapatkan InputStream dan OutputStream yang menangani transmisi melalui soket masing-masing menggunakan getInputStream() dan getOutputStream().
  2. Membaca dan menulis data ke aliran data menggunakan read(byte[]) dan write(byte[]).

Tentu saja, ada detail implementasi yang harus dipertimbangkan. Secara khusus, Anda harus menggunakan thread khusus untuk membaca dari aliran dan menulis ke aliran tersebut. Hal ini penting karena metode read(byte[]) dan write(byte[]) merupakan panggilan pemblokir. Metode read(byte[]) akan memblokir hingga ada sesuatu untuk dibaca dari aliran data. Metode write(byte[]) biasanya tidak memblokir, tetapi dapat memblokir kontrol alur jika perangkat jarak jauh tidak memanggil read(byte[]) dengan cukup cepat dan akibatnya buffer perantara menjadi penuh. Jadi, loop utama Anda dalam thread harus dikhususkan untuk membaca dari InputStream. Metode publik yang terpisah dalam thread dapat digunakan untuk memulai penulisan ke OutputStream.

Contoh

Berikut ini contoh cara mentransfer data antara dua perangkat yang terhubung melalui Bluetooth:

Kotlin

private const val TAG = "MY_APP_DEBUG_TAG"

// Defines several constants used when transmitting messages between the
// service and the UI.
const val MESSAGE_READ: Int = 0
const val MESSAGE_WRITE: Int = 1
const val MESSAGE_TOAST: Int = 2
// ... (Add other message types here as needed.)

class MyBluetoothService(
        // handler that gets info from Bluetooth service
        private val handler: Handler) {

    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) // mmBuffer store for the stream

        override fun run() {
            var numBytes: Int // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                // Read from the InputStream.
                numBytes = try {
                    mmInStream.read(mmBuffer)
                } catch (e: IOException) {
                    Log.d(TAG, "Input stream was disconnected", e)
                    break
                }

                // Send the obtained bytes to the UI activity.
                val readMsg = handler.obtainMessage(
                        MESSAGE_READ, numBytes, -1,
                        mmBuffer)
                readMsg.sendToTarget()
            }
        }

        // Call this from the main activity to send data to the remote device.
        fun write(bytes: ByteArray) {
            try {
                mmOutStream.write(bytes)
            } catch (e: IOException) {
                Log.e(TAG, "Error occurred when sending data", e)

                // Send a failure message back to the activity.
                val writeErrorMsg = handler.obtainMessage(MESSAGE_TOAST)
                val bundle = Bundle().apply {
                    putString("toast", "Couldn't send data to the other device")
                }
                writeErrorMsg.data = bundle
                handler.sendMessage(writeErrorMsg)
                return
            }

            // Share the sent message with the UI activity.
            val writtenMsg = handler.obtainMessage(
                    MESSAGE_WRITE, -1, -1, bytes)
            writtenMsg.sendToTarget()
        }

        // Call this method from the main activity to shut down the connection.
        fun cancel() {
            try {
                mmSocket.close()
            } catch (e: IOException) {
                Log.e(TAG, "Could not close the connect socket", e)
            }
        }
    }
}

Java

public class MyBluetoothService {
    private static final String TAG = "MY_APP_DEBUG_TAG";
    private Handler handler; // handler that gets info from Bluetooth service

    // Defines several constants used when transmitting messages between the
    // service and the UI.
    private interface MessageConstants {
        public static final int MESSAGE_READ = 0;
        public static final int MESSAGE_WRITE = 1;
        public static final int MESSAGE_TOAST = 2;

        // ... (Add other message types here as needed.)
    }

    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private byte[] mmBuffer; // mmBuffer store for the stream

        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the input and output streams; using temp objects because
            // member streams are final.
            try {
                tmpIn = socket.getInputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating input stream", e);
            }
            try {
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating output stream", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            mmBuffer = new byte[1024];
            int numBytes; // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                try {
                    // Read from the InputStream.
                    numBytes = mmInStream.read(mmBuffer);
                    // Send the obtained bytes to the UI activity.
                    Message readMsg = handler.obtainMessage(
                            MessageConstants.MESSAGE_READ, numBytes, -1,
                            mmBuffer);
                    readMsg.sendToTarget();
                } catch (IOException e) {
                    Log.d(TAG, "Input stream was disconnected", e);
                    break;
                }
            }
        }

        // Call this from the main activity to send data to the remote device.
        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);

                // Share the sent message with the UI activity.
                Message writtenMsg = handler.obtainMessage(
                        MessageConstants.MESSAGE_WRITE, -1, -1, bytes);
                writtenMsg.sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when sending data", e);

                // Send a failure message back to the activity.
                Message writeErrorMsg =
                        handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
                Bundle bundle = new Bundle();
                bundle.putString("toast",
                        "Couldn't send data to the other device");
                writeErrorMsg.setData(bundle);
                handler.sendMessage(writeErrorMsg);
            }
        }

        // Call this method from the main activity to shut down the connection.
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Could not close the connect socket", e);
            }
        }
    }
}

Setelah konstruktor mendapatkan aliran yang diperlukan, thread akan menunggu data masuk melalui InputStream. Saat read(byte[]) ditampilkan dengan data dari aliran, data akan dikirim ke aktivitas utama menggunakan Handler anggota dari class induk. Thread kemudian menunggu lebih banyak byte untuk dibaca dari InputStream.

Mengirim data keluar semudah memanggil metode write() thread dari aktivitas utama dan meneruskan byte yang akan dikirim. Metode ini memanggil write(byte[]) untuk mengirim data ke perangkat jarak jauh. Jika IOException ditampilkan saat memanggil write(byte[]), thread akan mengirimkan toast ke aktivitas utama, yang menjelaskan kepada pengguna bahwa perangkat tidak dapat mengirim byte tertentu ke perangkat lain (yang terhubung).

Metode cancel() thread memungkinkan koneksi dihentikan kapan saja dengan menutup BluetoothSocket. Metode ini harus selalu dipanggil setelah Anda selesai menggunakan koneksi Bluetooth.

Untuk mendemonstrasikan penggunaan Bluetooth API, lihat aplikasi contoh Bluetooth Chat.

Class dan antarmuka utama

Semua Bluetooth API tersedia dalam paket android.bluetooth. Berikut adalah ringkasan class dan antarmuka yang Anda perlukan untuk membuat koneksi Bluetooth:

BluetoothAdapter
Merepresentasikan adaptor Bluetooth lokal (radio Bluetooth). BluetoothAdapter adalah titik entri untuk semua interaksi Bluetooth. Dengan cara ini, Anda dapat menemukan perangkat Bluetooth lainnya, membuat kueri daftar perangkat terikat (disambungkan), membuat instance BluetoothDevice menggunakan alamat MAC yang dikenal, dan membuat BluetoothServerSocket untuk mendengarkan komunikasi dari perangkat lain.
BluetoothDevice
Merepresentasikan perangkat Bluetooth jarak jauh. Gunakan metode ini untuk meminta koneksi dengan perangkat jarak jauh melalui BluetoothSocket atau mengkueri informasi tentang perangkat seperti nama, alamat, class, dan status ikatannya.
BluetoothSocket
Merepresentasikan antarmuka untuk soket Bluetooth (mirip dengan TCP Socket). Ini adalah titik koneksi yang memungkinkan aplikasi bertukar data dengan perangkat Bluetooth lain menggunakan InputStream dan OutputStream.
BluetoothServerSocket
Merepresentasikan soket server terbuka yang memproses permintaan masuk (mirip dengan ServerSocket TCP). Untuk menghubungkan dua perangkat Android, satu perangkat harus membuka soket server dengan class ini. Saat perangkat Bluetooth jarak jauh membuat permintaan koneksi ke perangkat ini, perangkat akan menerima koneksi tersebut, lalu menampilkan BluetoothSocket yang terhubung.
BluetoothClass
Menjelaskan karakteristik dan kemampuan umum perangkat Bluetooth. Ini adalah kumpulan properti hanya baca yang menentukan class dan layanan perangkat. Meskipun informasi ini memberikan petunjuk yang berguna mengenai jenis perangkat, atribut class ini tidak selalu menjelaskan semua profil dan layanan Bluetooth yang didukung perangkat.
BluetoothProfile
Antarmuka yang merepresentasikan profil Bluetooth. Profil Bluetooth adalah spesifikasi antarmuka nirkabel untuk komunikasi berbasis Bluetooth antarperangkat. Contohnya adalah profil Hands-Free. Untuk diskusi selengkapnya tentang profil, lihat Bekerja dengan Profil.
BluetoothHeadset
Menyediakan dukungan untuk headset Bluetooth yang akan digunakan dengan ponsel. Hal ini mencakup profil Bluetooth Headset dan profil Hands-Free (v1.5).
BluetoothA2dp
Menentukan cara audio berkualitas tinggi dapat di-streaming dari satu perangkat ke perangkat lainnya melalui koneksi Bluetooth menggunakan Profil Distribusi Audio Lanjutan (A2DP).
BluetoothHealth
Mewakili proxy Profil Perangkat Kesehatan yang mengontrol layanan Bluetooth.
BluetoothHealthCallback
Class abstrak yang Anda gunakan untuk menerapkan callback BluetoothHealth. Anda harus memperluas class ini dan mengimplementasikan metode callback untuk menerima update tentang perubahan dalam status pendaftaran aplikasi dan status saluran Bluetooth.
BluetoothHealthAppConfiguration
Merepresentasikan konfigurasi aplikasi yang didaftarkan aplikasi pihak ketiga Bluetooth Health untuk berkomunikasi dengan perangkat kesehatan Bluetooth jarak jauh.
BluetoothProfile.ServiceListener
Antarmuka yang memberi tahu klien komunikasi antarproses (IPC) BluetoothProfile saat mereka telah terhubung ke atau terputus dari layanan internal yang menjalankan profil tertentu.