You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

请求Android BLE广播数据包示例代码及定向广播实现的带详解示例

Android BLE 定向广播实现详解(附示例代码)

Hey there! Let's clear up how to send targeted BLE broadcasts on Android, including the packet structure logic and a fully commented example code. First, a quick key point to wrap your head around:

BLE broadcasts are always sent to all nearby devices by design. "Targeted" broadcasting works by adding unique identifiers to your broadcast packet, so only your intended receiver will recognize and process it.


1. Core Logic: How "Targeted" BLE Broadcasts Work

Instead of sending directly to a specific device (BLE doesn't support native point-to-point broadcasts), we use a two-step approach:

  • Embed a unique marker in our broadcast packet (like a custom UUID or vendor-specific data)
  • Configure the target receiver to scan only for packets containing this marker

This way, even though every nearby device gets the broadcast, only your target app will act on it—giving the effect of "sending to a specific receiver."


2. BLE Broadcast Packet Structure Basics

Each BLE broadcast packet is made of multiple AD Structures (Advertising Data Structures). Each structure has 3 components:

  • Length: 1 byte (total length of the Type + Data fields)
  • Type: 1 byte (defines what the data represents, e.g., UUID, vendor-specific data)
  • Data: Variable length (the actual content we want to broadcast)

Common Type values we'll use in our example:

  • 0x01: Flags (e.g., indicates if the device is connectable)
  • 0x07: Complete 128-bit Service UUID (our unique identification marker)
  • 0xFF: Manufacturer Specific Data (custom data we can define for additional targeting)

3. Full Example Code with Detailed Explanations

Step 1: Add Permissions to AndroidManifest.xml

For Android 12+ (API 31+), you need specific runtime permissions. Older versions require location access for BLE scanning:

<!-- Legacy Bluetooth permissions (pre-Android 12) -->
<uses-permission android:name="android.permission.BLUETOOTH" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" android:maxSdkVersion="30" />

<!-- Android 12+ BLE permissions -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />

<!-- Location permission for pre-Android 12 devices -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" android:maxSdkVersion="30" />

<!-- Declare BLE hardware support -->
<uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />

Step 2: Main Activity Code

This code handles permission checks, Bluetooth initialization, and starting/stopping the targeted broadcast:

import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.le.AdvertiseCallback
import android.bluetooth.le.AdvertiseData
import android.bluetooth.le.AdvertiseSettings
import android.bluetooth.le.BluetoothLeAdvertiser
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import android.os.ParcelUuid

class BLEAdvertiserActivity : AppCompatActivity() {

    // Generate your own unique 128-bit UUID (use an online UUID generator!)
    private val CUSTOM_SERVICE_UUID = java.util.UUID.fromString("12345678-1234-5678-1234-567812345678")

    // Vendor ID (use your company's assigned ID, or 0x0000 for testing)
    private val VENDOR_ID = 0x0000
    // Custom target marker (receiver will check for this byte array)
    private val TARGET_MARKER = byteArrayOf(0xAB, 0xCD, 0xEF) // 3-byte unique identifier

    private var bluetoothLeAdvertiser: BluetoothLeAdvertiser? = null
    private var isAdvertising = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val startBtn = findViewById<Button>(R.id.start_advertise_btn)
        val stopBtn = findViewById<Button>(R.id.stop_advertise_btn)

        startBtn.setOnClickListener {
            if (checkPermissions()) {
                initBluetooth()
                startAdvertising()
            } else {
                requestPermissions()
            }
        }

        stopBtn.setOnClickListener {
            stopAdvertising()
        }
    }

    // Verify all required permissions are granted
    private fun checkPermissions(): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADVERTISE) == PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
        } else {
            ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
                    ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
        }
    }

    // Request missing permissions from the user
    private fun requestPermissions() {
        val permissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            arrayOf(
                Manifest.permission.BLUETOOTH_ADVERTISE,
                Manifest.permission.BLUETOOTH_SCAN,
                Manifest.permission.BLUETOOTH_CONNECT
            )
        } else {
            arrayOf(
                Manifest.permission.BLUETOOTH_ADMIN,
                Manifest.permission.BLUETOOTH,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        }
        ActivityCompat.requestPermissions(this, permissions, PERMISSION_REQUEST_CODE)
    }

    // Initialize Bluetooth adapter and advertiser
    private fun initBluetooth() {
        val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
        val bluetoothAdapter = bluetoothManager.adapter

        // Check if Bluetooth is enabled and supported
        if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled) {
            Toast.makeText(this, "Please enable Bluetooth first", Toast.LENGTH_SHORT).show()
            return
        }

        // Get the BLE advertiser instance
        bluetoothLeAdvertiser = bluetoothAdapter.bluetoothLeAdvertiser
    }

    // Start broadcasting our targeted BLE packet
    private fun startAdvertising() {
        if (bluetoothLeAdvertiser == null || isAdvertising) return

        // 1. Configure advertising settings (power, mode, timeout)
        val advertiseSettings = AdvertiseSettings.Builder()
            .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_POWER) // Battery-friendly for background use
            .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_MEDIUM) // Balanced range and power
            .setConnectable(false) // We don't need to accept connections (broadcast-only)
            .setTimeout(0) // Broadcast indefinitely (0 = no timeout)
            .build()

        // 2. Build the broadcast packet with our targeting markers
        val advertiseData = AdvertiseData.Builder()
            // Add flags: indicate we're a BLE-only, non-connectable device
            .addFlags(AdvertiseData.FLAG_BREDR_NOT_SUPPORTED)
            // Add our custom service UUID (receiver will scan for this)
            .addServiceUuid(ParcelUuid(CUSTOM_SERVICE_UUID))
            // Add vendor-specific data with our target marker
            .addManufacturerData(VENDOR_ID, TARGET_MARKER)
            .build()

        // 3. Start advertising with status callbacks
        bluetoothLeAdvertiser?.startAdvertising(advertiseSettings, advertiseData, advertiseCallback)
    }

    // Stop the BLE broadcast
    private fun stopAdvertising() {
        if (bluetoothLeAdvertiser != null && isAdvertising) {
            bluetoothLeAdvertiser?.stopAdvertising(advertiseCallback)
            isAdvertising = false
            Toast.makeText(this, "Stopped targeted broadcast", Toast.LENGTH_SHORT).show()
        }
    }

    // Callback to handle advertising success/failure
    private val advertiseCallback = object : AdvertiseCallback() {
        override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
            super.onStartSuccess(settingsInEffect)
            isAdvertising = true
            Toast.makeText(this@BLEAdvertiserActivity, "Started targeted BLE broadcast", Toast.LENGTH_SHORT).show()
        }

        override fun onStartFailure(errorCode: Int) {
            super.onStartFailure(errorCode)
            isAdvertising = false
            val errorMsg = when(errorCode) {
                ADVERTISE_FAILED_ALREADY_STARTED -> "Advertising already running"
                ADVERTISE_FAILED_DATA_TOO_LARGE -> "Broadcast data exceeds size limit"
                ADVERTISE_FAILED_FEATURE_UNSUPPORTED -> "BLE advertising not supported on this device"
                ADVERTISE_FAILED_INTERNAL_ERROR -> "Internal system error"
                ADVERTISE_FAILED_TOO_MANY_ADVERTISERS -> "Too many BLE advertisers nearby"
                else -> "Unknown error: $errorCode"
            }
            Toast.makeText(this@BLEAdvertiserActivity, "Advertising failed: $errorMsg", Toast.LENGTH_LONG).show()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        stopAdvertising() // Clean up when activity is closed
    }

    companion object {
        private const val PERMISSION_REQUEST_CODE = 1001
    }
}

4. Key Explanations for the Code

A. Targeting Mechanisms

  • Custom Service UUID: This is your app's unique fingerprint. The target receiver will set up a scan filter to only pick up broadcasts with this UUID, ignoring all others.
  • Vendor-Specific Data: The TARGET_MARKER byte array lets you add extra targeting logic—for example, sending a device ID or message that the receiver can parse to confirm it's the intended recipient.

B. How the Receiver Picks Up the Broadcast

On the receiver side, you'd set up a BLE scanner with a filter for your custom UUID. Here's a quick snippet:

val scanFilter = ScanFilter.Builder()
    .setServiceUuid(ParcelUuid(CUSTOM_SERVICE_UUID))
    .build()

Or, if using vendor data, you'd parse the scan result's manufacturer data to check for your TARGET_MARKER.

C. Important Notes

  • Connectable Mode: We set setConnectable(false) because we're only broadcasting data. If you need to establish a connection later, set this to true.
  • Power Modes: Use ADVERTISE_MODE_LOW_POWER for background broadcasting to save battery. Use ADVERTISE_MODE_HIGH_POWER if you need faster detection.
  • Device Support: Not all Android devices support BLE advertising. The onStartFailure callback will notify you if it's unsupported.

内容的提问来源于stack exchange,提问作者user9738849

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:57:51