active
とpassive
スキャンの違いは、スキャンが広告主からのパケットをactive
要求することです。SCAN_RESPONSE
これはSCAN_REQUEST
、広告が検出された後にパケットを送信することによって行われます。両方の情報 (ペイロード) はscanRecord
、デバイスが見つかったコールバックのパラメーターになります。
コア仕様から:
デバイスは、アクティブ スキャンを使用して、ユーザー インターフェイスの設定に役立つデバイスに関する詳細情報を取得できます。アクティブ スキャンには、より多くのリンク レイヤ アドバタイジング メッセージが含まれます。
したがって、どのようなユースケースでも、これら 2 つのスキャン タイプを区別する必要はありません。
ただし、バックグラウンドで広告をリッスンする場合Service
は、組み込みの機能はありません (Android 4.4 以降) を作成して、自分でこれを行う必要があります。
バックグラウンド スキャンの場合、この例を取り上げます。ただし、アプリがシステムによって強制終了された (またはユーザーによって停止された) 時点で、スキャンは終了します。
AlarmManager を介して PendingIntent を開始します (アプリ内の任意の場所で、サービスを開始するために少なくとも 1 回実行する必要があります...)
AlarmManager alarmMgr = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(getActivity(), BleScanService.class);
PendingIntent scanIntent = PendingIntent.getService(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, SystemClock.elapsedRealtime(), intervalMillis, scanIntent);
BleScan サービス
public class BleScanService extends Service implements LeScanCallback {
private final static String TAG = BleScanService.class.getSimpleName();
private final IBinder mBinder = new LocalBinder();
private BluetoothManager mBluetoothManager;
private BluetoothAdapter mBluetoothAdapter;
public class LocalBinder extends Binder {
public BleScanService getService() {
return BleScanService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
return super.onUnbind(intent);
}
@Override
public void onCreate() {
super.onCreate();
initialize();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
long timeToScan = preferences.scanLength().get();
startScan(timeToScan);
return super.onStartCommand(intent, flags, startId);
}
/**
* Initializes a reference to the local bluetooth adapter.
*
* @return Return true if the initialization is successful.
*/
public boolean initialize() {
// For API level 18 and above, get a reference to BluetoothAdapter
// through
// BluetoothManager.
if (mBluetoothManager == null) {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (mBluetoothManager == null) {
Log.e(TAG, "Unable to initialize BluetoothManager.");
return false;
}
}
if (mBluetoothAdapter == null) {
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (mBluetoothAdapter == null) {
Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
return false;
}
}
Log.d(TAG, "Initialzed scanner.");
return true;
}
/**
* Checks if bluetooth is correctly set up.
*
* @return
*/
protected boolean isInitialized() {
return mBluetoothManager != null && mBluetoothAdapter != null && mBluetoothAdapter.isEnabled();
}
/**
* Checks if ble is ready and bluetooth is correctly setup.
*
* @return
*/
protected boolean isReady() {
return isInitialized() && isBleReady();
}
/**
* Checks if the device is ble ready.
*
* @return
*/
protected boolean isBleReady() {
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE);
}
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
Log.d(TAG, "Found ble device " + device.getName() + " " + device.getAddress());
broadcastOnDeviceFound(device, scanRecord);
}
/**
* Broadcasts a message with the given device.
*
* @param device
* @param scanRecord
*/
protected void broadcastOnDeviceFound(final BluetoothDevice device, byte[] scanRecord) {
assert device != null : "Device should not be null.";
Intent intent = new Intent(BleServiceConstants.ACTION_DEVICE_DISCOVERED);
intent.putExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_DEVICE, device);
intent.putExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_SCAN_RECORD, scanRecord);
sendBroadcast(intent);
}
/**
* Starts the bluetooth low energy scan It scans at least the
* delayStopTimeInMillis.
*
* @param delayStopTimeInMillis
* the duration of the scan
* @return <code>true</code> if the scan is successfully started.
*/
public boolean startScan(long delayStopTimeInMillis) {
if (!isReady())
return false;
if (preferences.shouldScan().get()) {
if (delayStopTimeInMillis <= 0) {
Log.w(TAG, "Did not start scanning with automatic stop delay time of " + delayStopTimeInMillis);
return false;
}
Log.d(TAG, "Auto-Stop scan after " + delayStopTimeInMillis + " ms");
getMainHandler().postDelayed(new Runnable() {
@Override
public void run() {
Log.d(TAG, "Stopped scan.");
stopScan();
}
}, delayStopTimeInMillis);
}
return startScan();
}
/**
* @return an handler with the main (ui) looper.
*/
private Handler getMainHandler() {
return new Handler(getMainLooper());
}
/**
* Starts the bluetooth low energy scan. It scans without time limit.
*
* @return <code>true</code> if the scan is successfully started.
*/
public boolean startScan() {
if (!isReady())
return false;
if (preferences.shouldScan().get()) {
if (mBluetoothAdapter != null) {
Log.d(TAG, "Started scan.");
return mBluetoothAdapter.startLeScan(this);
} else {
Log.d(TAG, "BluetoothAdapter is null.");
return false;
}
}
return false;
}
/**
* Stops the bluetooth low energy scan.
*/
public void stopScan() {
if (!isReady())
return;
if (mBluetoothAdapter != null)
mBluetoothAdapter.stopLeScan(this);
else {
Log.d(TAG, "BluetoothAdapter is null.");
}
}
@Override
public void onDestroy() {
preferences.edit().shouldScan().put(false).apply();
super.onDestroy();
}
}
定数は、インテント アクションと追加の名前を配布する単なる文字列です。スキャンフェーズの長さを保存する別の設定ストアがあります...必要に応じて簡単に置き換えることができます。
次に、上記のアクション名に一致するインテント フィルタを使用してブロードキャスト レシーバを登録する必要があります ( BleServiceConstants.ACTION_DEVICE_DISCOVERED
) 。
public class DeviceWatcher extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(BleServiceConstants.EXTRA_DEVICE_DISCOVERED_DEVICE);
// do anything with this information
}
}