Add custom data while advertising by using AltBeac

2019-04-17 09:06发布

问题:

I am learning Android app development while building an app that interacts with the physical web. I want to advertise based on AltBeacon specification and add small payload in the form of string or bytes. I came across Beacon.Builder Javadoc but it is not well written. Which function should I use from the above javadoc or is there something else available?

My goal is to advertise using my app and transmit certain string. If the other device has my app then it should show notification with that string. All my code has been taken from this.

回答1:

Standard bluetooth beacon layouts (iBeacon, AltBeacon, Eddystone-UID) are designed to transmit a unique numeric identifier, not a string. (Eddystone-URL is a bit of an exception as it is designed to transmit an encoded URL string.)

However, it is certainly possible to transmit a string encoded in a beacon transmission. Just understand that there are a few limits to this:

  1. The amount of space is limited. Only about 20 bytes of usable space is available in beacon transmissions, which is 20 characters if using an encoding like ASCII, and possibly fewer depending on the characters if using UTF-8.

  2. You need a certain number of matching bytes to figure out (with some reasonable certainty) if the advertisement is "yours" and should be decoded as a string at all. Using a two byte matching value would give you a 1 in 65536 chance of accidentally decoding somebody else's beacon as a string.

  3. If you want to receive and decode on iOS, you can't really use iBeacon, because a 16 byte UUID must be used for matching, leaving two few bytes left over to hold the string.

Here's an example of how you could do this using a modified AltBeacon layout, with a first two byte matching identifier of 0x8b9c used to make sure it is your beacon, and the second 18 byte identifier used to store an encoded string. The code snippet that shows transmitting an ASCII string that is a maximum of 18 characters long:

public static final Identifier MY_MATCHING_IDENTIFIER = Identifier.fromInt(0x8b9c);
...
mBeaconManager.getBeaconParsers().clear();
BeaconParser customBeaconParser = new BeaconParser().setBeaconLayout("m:2-3=beac,i:4-5,i:6-23,p:24-24,d:25-25");
mBeaconManager.addBeaconParser(customBeaconParser);
String stringToTransmit = "Only 18 chars fit!";
byte[] stringToTransmitAsAsciiBytes = stringToTransmit.getBytes(StandardCharsets.US_ASCII);
Beacon beacon = new Beacon.Builder().setId1(MY_MATCHING_IDENTIFIER.toString())
            .setId2(Identifier.fromBytes(stringToTransmitAsAsciiBytes, 0, 18, false).toString())
            .setTxPower(-59).build();
mBeaconTransmitter = new BeaconTransmitter(this, customBeaconParser);
mBeaconTransmitter.startAdvertising(beacon);

And here's receiving it:

@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
    for (Beacon beacon : beacons) {
        if (beacon.getId1().equals(MY_MATCHING_IDENTIFIER)) {
            byte[] bytes = beacon.getId2().toByteArray();
            String receivedString = null;
            try {
                receivedString = new String(bytes, 0, bytes.length, "ASCII");
            } catch (UnsupportedEncodingException e) {
                Log.d(TAG, "Cannot decode ASII");
            }
            Log.d(TAG, "I just received: "+receivedString);
        }
    }
}

EDIT: Make sure you clear the beacon parsers as shown above otherwise your code will try to use the default beacon parser. See code changes above.