尽管是一种相对较新的技术,但低功耗蓝牙(LE)已证明自己是一种通用且有用的通信介质。 尽管它可以用于无线连接设备,但它也使设备能够充当信标和广播数据。
在本教程中,您将学习BluetoothLeAdvertiser
类,该类使开发人员无需额外的硬件即可将受支持的电话变成Bluetooth LE信标。 您还将学习如何扫描Bluetooth LE广告数据,以便可以在自己的应用程序中做出适当的反应。 您可以在GitHub上下载本教程的源文件。
1.项目设置
对于本教程,您可以从在Android Studio中创建一个基本的空项目开始。 您需要在build.gradle文件中将最低SDK版本设置为21 ,因为直到Lollipop发行版才在Android上引入了蓝牙LE广告。 尽管有一些方法可以包装在SDK 21之前不支持的API,但本教程将不介绍它们。 我们只专注于手头的新技术。
defaultConfig {
applicationId "com.tutsplus.bleadvertising"
minSdkVersion 21
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
接下来,您需要向项目的AndroidManifest.xml添加三个权限。 前两个允许蓝牙通信,第三个许可ACCESS_COARSE_LOCATION
是在Android 6.0及更高版本的设备上使用蓝牙的新要求。
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
如果您在运行Android 6.0或更高版本的设备上进行开发,则您的应用需要用户授予的位置权限。 可以在我今年早些时候写的《 了解Android M中的权限》中找到有关执行此操作的说明。 为了简单起见,本教程将仅在Android App Info屏幕上授予权限。
既然您已经有权访问应用程序所需的所有内容,那么现在该创建布局文件了。 打开activity_main.xml并使用两个按钮和一个文本视图创建一个简单的布局。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/advertise_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Advertise"/>
<Button
android:id="@+id/discover_btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Discover" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="test"/>
</LinearLayout>
这两个Button
对象用于启动广告或发现。 TextView
用于显示发现时发现的数据。 完成填写布局文件后,可以将其关闭并打开MainActivity.java 。 在此文件中要做的第一件事是添加按钮所使用的View.OnClickListener
的实现。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public void onClick(View v) {
if( v.getId() == R.id.discover_btn ) {
discover();
} else if( v.getId() == R.id.advertise_btn ) {
advertise();
}
}
}
在上面的onClick(View v)
方法中,本教程后面将定义advertise()
和discover()
。 您现在可以为这些方法添加存根,以便您的应用进行编译。 接下来,您需要在类的顶部创建成员变量,以跟踪按钮和文本视图。
private TextView mText;
private Button mAdvertiseButton;
private Button mDiscoverButton;
定义视图后,需要在onCreate(Bundle savedInstanceState)
对其进行初始化,并将每个Button
连接到OnClickListener
定义的OnClickListener
。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mText = (TextView) findViewById( R.id.text );
mDiscoverButton = (Button) findViewById( R.id.discover_btn );
mAdvertiseButton = (Button) findViewById( R.id.advertise_btn );
mDiscoverButton.setOnClickListener( this );
mAdvertiseButton.setOnClickListener( this );
}
最后,请确保您或您的用户使用的设备支持多个广告。 虽然Lollipop可以提供外围设备广告所需的软件功能,但制造商还必须使用支持广告的蓝牙芯片组。
如果不支持Bluetooth LE广告,则应禁用应用程序中的两个按钮。 虽然已知此功能可在Nexus 6、5X,6P和9上使用,但其他手机也支持Bluetooth LE广告,随着制造商生产更新的手机和平板电脑,将继续制造其他设备。
if( !BluetoothAdapter.getDefaultAdapter().isMultipleAdvertisementSupported() ) {
Toast.makeText( this, "Multiple advertisement not supported", Toast.LENGTH_SHORT ).show();
mAdvertiseButton.setEnabled( false );
mDiscoverButton.setEnabled( false );
}
2.通过蓝牙LE投放广告
要启动广告通过蓝牙LE,你需要检索BluetoothLeAdvertiser
从Android BluetoothAdapter
。 您可以在点击广告按钮时调用的advertise()
方法中执行此操作。
BluetoothLeAdvertiser advertiser = BluetoothAdapter.getDefaultAdapter().getBluetoothLeAdvertiser();
有了BluetoothLeAdvertiser
,您就可以定义在广告中使用的设置,例如广播时天线应使用的电量以及设备应多久进行一次广告宣传。 AdvertiseSettings.Builder
类还允许您定义移动设备是否应可连接,从而可以跨设备流式传输更多数据。
AdvertiseSettings settings = new AdvertiseSettings.Builder()
.setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY )
.setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
.setConnectable( false )
.build();
您会注意到,此示例在广告中具有较高的信号强度(发射功率)和较低的延迟。 虽然这可以使您的设备快速被发现,但它也可能会占用大量电池,这在移动空间中发展时是宝贵的商品。
广告模式还有其他选项。 ADVERTISE_MODE_LOW_POWER
选项是广告的默认设置,并以最少的频率传输以节省最大电量。 ADVERTISE_MODE_BALANCED
选项尝试节省功率,而不必在广告之间等待太久。
可以将TX功率设置为超低,低,中或高。 每个级别对应于蓝牙LE广告包可见的程度,尽管确切范围取决于设备硬件。
接下来,定义一个UUID,用于在发布数据包时标识它们。 您可以从命令行使用uuidgen实用程序来创建新的UUID。
➜ ~ uuidgen
CDB7950D-73F1-4D4D-8E47-C090502DBD63
虽然此实用程序创建了128位的UUID,但Android系统仅使用16位的UUID进行广告宣传,并将自动调整128位的UUID以使其符合要求。 在上面的示例中,16位UUID为950D。 接下来,将128位UUID另存为strings.xml中的字符串 。
<string name="ble_uuid">CDB7950D-73F1-4D4D-8E47-C090502DBD63</string>
一旦创建了UUID,就该创建ParcelUuid
, AdvertiseData
对象,并广播一些附加数据作为附加服务了。 在此示例中,您仅广播了设备的名称和字符串"Data"
(必须将其转换为字节数组),因为您的广告数据包的大小相当有限。
ParcelUuid pUuid = new ParcelUuid( UUID.fromString( getString( R.string.ble_uuid ) ) );
AdvertiseData data = new AdvertiseData.Builder()
.setIncludeDeviceName( true )
.addServiceUuid( pUuid )
.addServiceData( pUuid, "Data".getBytes( Charset.forName( "UTF-8" ) ) )
.build();
使用设备通过Bluetooth LE进行广告的最后一件事是创建一个回调,侦听广告时是否成功,然后再通过BluetoothLeAdvertiser
广告。
AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
super.onStartSuccess(settingsInEffect);
}
@Override
public void onStartFailure(int errorCode) {
Log.e( "BLE", "Advertising onStartFailure: " + errorCode );
super.onStartFailure(errorCode);
}
};
advertiser.startAdvertising( settings, data, advertisingCallback );
此时,您应该能够运行您的应用并开始通过Bluetooth LE进行广告。 但是,本教程的下一步将涉及发现广告,以便您可以显示该信息。
3.发现蓝牙LE广告
在开始发现Bluetooth LE服务之前,您需要在课程顶部添加一些其他成员变量。
private BluetoothLeScanner mBluetoothLeScanner;
private Handler mHandler = new Handler();
mBluetoothLeScanner
用于扫描蓝牙LE数据包,并且mHandler
控制一个小的计时器,该计时器在设置的时间段后停止发现。
除了这些成员变量之外,您还需要在类的顶部创建一个新的ScanCallback
,以便稍后内部类可以在应用程序中对其进行访问。 此回调接收发现的可接受广告的结果,并在文本视图中显示广告设备名称和关联的数据。
private ScanCallback mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if( result == null
|| result.getDevice() == null
|| TextUtils.isEmpty(result.getDevice().getName()) )
return;
StringBuilder builder = new StringBuilder( result.getDevice().getName() );
builder.append("\n").append(new String(result.getScanRecord().getServiceData(result.getScanRecord().getServiceUuids().get(0)), Charset.forName("UTF-8")));
mText.setText(builder.toString());
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
Log.e( "BLE", "Discovery onScanFailed: " + errorCode );
super.onScanFailed(errorCode);
}
};
接下来,如下所示在onCreate(Bundle savedInstanceState)
初始化mBluetoothLeScanner
。
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();
初始化蓝牙扫描器后,您可以开始充实discover()
方法。 对于此示例应用程序,您将创建一个ScanFilter
对象并将其放入List
以便您的应用程序仅响应您感兴趣的广告包。
ScanFilter filter = new ScanFilter.Builder()
.setServiceUuid( new ParcelUuid(UUID.fromString( getString(R.string.ble_uuid ) ) ) )
.build();
filters.add( filter );
您还需要创建一个ScanSettings
对象,该对象的工作原理与您在本教程前面创建的AdvertiseSettings
对象相似。
ScanSettings settings = new ScanSettings.Builder()
.setScanMode( ScanSettings.SCAN_MODE_LOW_LATENCY )
.build();
设置好过滤器,设置和回调后,即可开始发现Bluetooth LE广告。
mBluetoothLeScanner.startScan(filters, settings, mScanCallback);
如果现在在两个都支持Bluetooth LE广告的设备上运行您的应用程序,并且将其中一个设置为做广告,而另一个设置为进行发现,则发现设备应找到广告商并显示广告的数据。 以下屏幕截图显示了Nexus 5X发现了广告Nexus 6。
您需要做的最后一件事是创建一个新的Runnable
对象,并将其与mHandler
关联。 处理程序等待十秒钟,然后启动可停止发现的Runnable
。 原因是发现会很快耗尽设备的电池。 您只想尝试在短时间内或期望找到广告设备时发现广告包。
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mBluetoothLeScanner.stopScan(mScanCallback);
}
}, 10000);
结论
尽管该示例可能看起来不多,但您刚刚创建了一个可以正常工作的应用程序,该应用程序通过Bluetooth LE进行广告发布,以便其他设备可以发现它并传输数据。 尽管本教程介绍了如何通过Android进行广告和发现,但是您也可以轻松地为iOS创建类似的应用,以便在两个平台之间共享数据或构建自己的支持蓝牙LE的设备,从而可以与手机和平板电脑进行交互。
随着这项技术的不断发展并将其集成到每个人的口袋中,开发人员将有无穷的可能性制作出真正有趣的东西。 有关在Android上使用其他蓝牙技术的更多信息,请查看Matthew Kim的 使用Android的蓝牙API创建蓝牙扫描仪 。
翻译自: https://code.tutsplus.com/tutorials/how-to-advertise-android-as-a-bluetooth-le-peripheral--cms-25426