Android 中的 BLE 广告
Posted
技术标签:
【中文标题】Android 中的 BLE 广告【英文标题】:BLE Advertisement in Android 【发布时间】:2016-04-20 17:37:52 【问题描述】:我正在开发和应用程序以在 android 中发送 BLE 广告数据包。我已经使用 AdvertiseData 和 AdverstiseSettings 类来生成广告数据包。但是当我执行 StartAdvertising 时,它总是给我一个错误代码“2”,“ADVERTISE_FAILED_TOO_MANY_ADVERTISERS”,“无法开始广告,因为没有可用的广告实例。”
下面是我的 MainActivity.JAVA 代码
package rockwellcollins.blutooth_advertise;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.le.AdvertiseCallback;
import android.bluetooth.le.AdvertiseData;
import android.bluetooth.le.AdvertiseSettings;
import android.bluetooth.le.BluetoothLeAdvertiser;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.os.Bundle;
import android.os.ParcelUuid;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
import java.util.UUID;
public class MainActivity extends AppCompatActivity
private BluetoothLeScanner mBluetoothLeScanner;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View view)
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
);
textView = (TextView) findViewById(R.id.txtv);
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
if( !BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported() )
Toast.makeText(this, "Multiple advertisement not supported", Toast.LENGTH_SHORT).show();
advertise();
BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner().startScan(scanCallback);
private void advertise()
BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY )
.setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
.setConnectable(false)
.build();
Log.i("BLE","start of advertise data after settings");
ParcelUuid pUuid = new ParcelUuid( UUID.fromString("b161c53c-0715-11e6-b512-3e1d05defe78"));
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName( true )
.setIncludeTxPowerLevel(true)
.addServiceUuid( pUuid )
//.addServiceData( pUuid, "Data".getBytes(Charset.forName("UTF-8") ) )
.build();
Log.i("BLE","before callback");
AdvertiseCallback advertisingCallback = new AdvertiseCallback()
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect)
super.onStartSuccess(settingsInEffect);
Log.i("BLE", "LE Advertise success.");
@Override
public void onStartFailure(int errorCode)
Log.e("BLE", "Advertising onStartFailure: " + errorCode);
super.onStartFailure(errorCode);
;
advertiser.startAdvertising( settings, data, advertisingCallback );
Log.i("BLE", "start advertising");
private final ScanCallback scanCallback = new ScanCallback()
@Override
public void onScanResult(int callbackType, ScanResult result)
printScanResult(result);
@Override
public void onBatchScanResults(List<ScanResult> results)
textView.append("Received " + results.size() + " batch results:\n");
for (ScanResult r : results)
printScanResult(r);
@Override
public void onScanFailed(int errorCode)
switch (errorCode)
case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
textView.append("Scan failed: already started.\n");
break;
case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
textView.append("Scan failed: app registration failed.\n");
break;
case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED:
textView.append("Scan failed: feature unsupported.\n");
break;
case ScanCallback.SCAN_FAILED_INTERNAL_ERROR:
textView.append("Scan failed: internal error.\n");
break;
private void printScanResult(ScanResult result)
String id = result.getDevice() != null ? result.getDevice().getAddress() : "unknown";
int tx = result.getScanRecord() != null ? result.getScanRecord().getTxPowerLevel() : 0;
textView.append("TX: " + tx + " RX: " + result.getRssi() + " from " + id+ ".\n");
;
@Override
public boolean onCreateOptionsMenu(Menu menu)
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
@Override
public boolean onOptionsItemSelected(MenuItem item)
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings)
return true;
return super.onOptionsItemSelected(item);
Android Manifest.xml 的代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="rockwellcollins.blutooth_advertise">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
能否请您告诉我我做错了什么以及如何解决此错误?
谢谢
【问题讨论】:
【参考方案1】:根据我的经验,关于 BLE 广告的 Android 设备有 4 种类型:
-
Android pre-5.0 的设备 - 不支持 LE 广告
Android 5+ 的设备不支持 LE 广告并从 getBluetoothLeAdvertiser() 返回 null。这些设备从isMultipleAdvertisementSupported() 返回false。他们甚至在蓝牙开启的情况下也会这样做(请参阅下面的注释)。
Android 5+ 设备返回 BluetoothLeAdvertiser 对象,但每次广告尝试都以 ADVERTISE_FAILED_TOO_MANY_ADVERTISERS 错误结束(您就是这种情况)。这些设备从isMultipleAdvertisementSupported() 返回true,如您所见,这是不正确的。到目前为止,我只看到过一款属于此类别的手机:Sony xperia z1 compact,但如果有的话,还有更多。
支持 LE 广告的 Android 5+ 设备。那些从isMultipleAdvertisementSupported() 返回 true 但仅在蓝牙开启时。
注意:在 2.、3. 和 4. 中,BluetoothLeAdvertiser 对象仅在蓝牙开启时返回。否则返回 null,因此在启用蓝牙之前,您实际上不知道设备是否支持 LE 广告。
检查 nRF Connect 应用程序:禁用蓝牙,安装应用程序,打开并选择广告商选项卡或导航菜单 -> 设备信息。在显示状态之前,它会要求您打开蓝牙。
【讨论】:
我目前在不属于任何这些类型的设备上进行测试,Huawei Mate 20 lite: isMultipleAdvertisementSupported() 返回 false,即使它确实允许广告,但一次只能有一个(因此它是正确的,因为它不允许多个)。 @JorgeGalvão 我在将荣耀 10 更新到 Android 10 时遇到了同样的问题。如果设备具有广告功能,但没有多重广告功能,您如何开始投放广告? @giuseppe_para 很抱歉直到现在才错过你的问题......我认为我从来没有解决过这个问题,因为我放弃了这个项目。我认为我什至没有处理getBluetoothLeAdvertiser()
将返回 null 的情况。就我看到的代码而言,我刚刚测试了getSystemService(Context.BLUETOOTH_SERVICE)
,它的getAdapter()
为null。然后我还检查了AdvertiseCallback
的onStartFailure
回调。
@giuseppe_para 您可以在此处检查 ForegroundService.java 和 BluetoothLeAdvertiseHelperLollipop.java(它总体上工作正常,但我从未针对许多设备进行过测试):github.com/Track-COVID-19/Track-COVID-19-Android/tree/develop/…
谢谢!我不再在这个项目上工作了,但是当我再次工作时,我会尝试这个解决方案。【参考方案2】:
请参阅this question 以获得可能的答案,并非所有设备都支持 BLE 广告。
还可以尝试按照建议的here省略设备名称。
【讨论】:
我检查了蓝牙 LE 功能,它在我的设备上可用。PackageManager manager = getApplicationContext().getPackageManager(); boolean featureAvailable = manager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE); if(!featureAvailable) Toast.makeText(this, "Bluetooth LE feature is not available", Toast.LENGTH_SHORT).show();
仅蓝牙 LE 功能是不够的。并非所有具有蓝牙 LE 的设备都支持该广告。有一个list of compatible devices,如果您尝试this app from Radius,您可以测试您的设备是否可以做广告。
其实我用的是Qualcomm eval kit for Android,APQ 8084,它有蓝牙4.1,这意味着它支持LE模式。
您是否查看了@Markus Kauppinen 建议的应用程序?
我检查了它,但每次点击信标发射器时它都会崩溃。另外,通过我的代码,我看到“不支持多个广告”的 ToastMessage,这是否意味着我无法传输信标?【参考方案3】:
您只需在您的方法上添加此代码:@TargetApi(Build.VERSION_CODES.M)
【讨论】:
以上是关于Android 中的 BLE 广告的主要内容,如果未能解决你的问题,请参考以下文章