Android中的BLE广告 [英] BLE Advertisement in Android

查看:176
本文介绍了Android中的BLE广告的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在开发和应用程序来在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;

公共类MainActivity扩展了AppCompatActivity {
private BluetoothLeScanner mBluetoothLeScanner;
私人TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
工具栏工具栏=(工具栏)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,用自己的动作代替 ,Snackbar.LENGTH_LONG)
.setAction( Action,null).show();
}
});

textView =(TextView)findViewById(R.id.txtv);
mBluetoothLeScanner = BluetoothAdapter.getDefaultAdapter()。getBluetoothLeScanner();

if(!BluetoothAdapter.getDefaultAdapter()。isMultipleAdvertisementSupported()){
Toast.makeText(this,不支持多个广告,Toast.LENGTH_SHORT).show();
}
advertise();
BluetoothAdapter.getDefaultAdapter()。getBluetoothLeScanner()。startScan(scanCallback);
}

private void advertise(){
BluetoothLeAdvertiser广告客户= BluetoothAdapter.getDefaultAdapter()。getBluetoothLeAdvertiser();

AdvertiseSettings设置= new AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
.setable $ b .build();
Log.i( BLE,设置后开始发布数据);
ParcelUuid pUuid =新的ParcelUuid(UUID.fromString( b161c53c-0715-11e6-b512-3e1d05defe78));

AdvertiseData数据= new AdvertiseData.Builder()
.setIncludeDeviceName(true)
.setIncludeTxPowerLevel(true)
.addServiceUuid(pUuid)
// .addServiceData(pUuid, Data .getBytes(Charset.forName( UTF-8)))
.build();
Log.i( BLE,回调之前);
AdvertiseCallback advertisingCallback = new AdvertiseCallback(){
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect){
super.onStartSuccess(settingsInEffect);
Log.i( BLE, LE广告成功。);

}

@Override
public void onStartFailure(int errorCode){
Log.e( BLE, Advertising onStartFailure: + errorCode );
super.onStartFailure(errorCode);
}
};

advertiser.startAdvertising(设置,数据,advertisingCallback);
Log.i( BLE,开始广告);
}

私有最终ScanCallback scanCallback = new ScanCallback(){
@Override
public void onScanResult(int callbackType,ScanResult result){
printScanResult(结果);
}

@Override
public void onBatchScanResults(List< ScanResult> results){
textView.append( Received + results.size()+ batch结果:\n);
for(ScanResult r:results){
printScanResult(r);
}
}

@Override
public void onScanFailed(int errorCode){
switch(errorCode){
case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
textView.append(扫描失败:已开始。\n);
休息时间;
case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
textView.append(扫描失败:应用程序注册失败。\n);
休息时间;
case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED:
textView.append(扫描失败:功能不受支持。\n);
休息时间;
case ScanCallback.SCAN_FAILED_INTERNAL_ERROR:
textView.append(扫描失败:内部错误。\n);
休息时间;
}
}

private void printScanResult(ScanResult result){
字符串ID = result.getDevice()!= null吗? result.getDevice()。getAddress():未知;
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){
//膨胀菜单;这会将项目添加到操作栏(如果有)。
getMenuInflater()。inflate(R.menu.menu_main,menu);
返回true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item){
//处理操作栏项单击此处。当您在AndroidManifest.xml中指定父活动时,操作栏将自动处理
//单击Home / Up按钮,因此
//很长。
int id = item.getItemId();

//无检查SimplifiableIfStatement
if(id == R.id.action_settings){
返回true;
}

return super.onOptionsItemSelected(item);
}
}

Android Manifest.xml的代码

 <?xml version = 1.0 encoding = utf-8?> 
<清单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 />
<应用程序
android:allowBackup = true
android:icon = @ mipmap / ic_launcher
android:label = @ string / app_name
android:supportsRtl = true
android:theme = @ style / AppTheme>
<活动
android:name =。MainActivity
android:label = @ string / app_name
android:theme = @ style / AppTheme.NoActionBar> ;
<意图过滤器>
< action android:name = android.intent.action.MAIN />

< category android:name = android.intent.category.LAUNCHER />
< / intent-filter>
< / activity>
< / application>

< / manifest>

能否让我知道我做错了什么以及如何解决此错误? p>

谢谢

解决方案

根据我的经验,有4种类型的Android设备考虑BLE广告:


  1. 装有Android 5.0之前版本的设备-不支持LE广告

  2. 具有Android 5.0以下版本的设备不支持LE广告的Android 5+并从null rel = noreferrer> getBluetoothLeAdvertiser()。这些设备从 isMultipleAdvertisementSupported()返回 false 。他们甚至可以在打开蓝牙的情况下执行此操作(请参见下面的注释)。

  3. 装有Android 5+的设备返回 BluetoothLeAdvertiser 对象,但每次尝试广告均以 ADVERTISE_FAILED_TOO_MANY_ADVERTISERS 错误(情况就是这样)。这些设备从 isMultipleAdvertisementSupported()返回 true 如您所见,这是不正确的。到目前为止,我只看到该类别中的一部手机:Sony xperia z1 compact,但如果有一部,还会有更多。

  4. 支持LE Advertisement的Android 5+设备。那些从 isMultipleAdvertisementSupported(),但仅当蓝牙打开时。

注意:在2.,3和4中, BluetoothLeAdvertiser 对象仅在蓝牙打开时返回。否则,将返回 null ,因此,在启用蓝牙之前,您实际上不知道设备是否支持LE Advertisement。



检查nRF Connect应用程序:禁用蓝牙,安装该应用程序,打开并选择Advertiser标签或Navigation菜单-> Device information。在显示状态之前,它将要求您打开蓝牙。


I am developing and app to Send BLE Advertisement packet in android. I have use AdvertiseData and AdverstiseSettings classes to generate the advertise packet. But when i do the StartAdvertising it always gives me an error with Error Code "2" , "ADVERTISE_FAILED_TOO_MANY_ADVERTISERS", "Failed to start advertising because no advertising instance is available."

Below is my code for 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);
    }
}

Code for 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>

Could you please let me know what I am doing wrong and how can I solve this error?

Thanks

解决方案

From my experience there are 4 types of Android devices in regard BLE advertisement:

  1. Devices with Android pre-5.0 - LE Advertisement not supported
  2. Devices with Android 5+ that don't support LE Advertisement and return null from getBluetoothLeAdvertiser(). Those devices return false from isMultipleAdvertisementSupported(). They do this even with Bluetooth ON (see Note below).
  3. Devices with Android 5+ that return the BluetoothLeAdvertiser object, but each try of advertising ends with ADVERTISE_FAILED_TOO_MANY_ADVERTISERS error (this is the case you have). Those devices return true from isMultipleAdvertisementSupported() which as you see is not true. So far I've seen only one phone from this category: Sony xperia z1 compact, but if there is one, there are more.
  4. Devices with Android 5+ that support LE Advertisement. Those return true from isMultipleAdvertisementSupported() but ONLY when Bluetooth is ON.

Note: in the 2., 3. and 4. the BluetoothLeAdvertiser object is returned ONLY when Bluetooth is ON. Otherwise null is returned, so you actually have no clue whether the device supports LE Advertisement or not until Bluetooth is enabled.

Check the nRF Connect app: Disable Bluetooth, install the app, open and select Advertiser tab or Navigation menu -> Device information. It will ask you to turn Bluetooth ON before the status will be shown.

这篇关于Android中的BLE广告的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆