android - 如何对BLE设备进行后台扫描?

标签 android service bluetooth-lowenergy

我目前正在做一个项目,需要 Android 手机应用程序对特定 SensorTag 进行后台扫描,并触发通知以提醒用户 sanitizer 双手。

我已成功创建应用程序,并且可以在应用程序运行时收到通知消息。我的应用程序可以扫描 BLE 设备并使用列表适配器显示它们,并且可以以 10 秒的间隔进行扫描。

但是,当手机处于 sleep 状态或使用其他应用程序时,我无法收到任何通知消息,这有点违背了我项目的目的。

如果有人能启发我,我将不胜感激。

谢谢

克里丝琳

*附注我仅在广播模式和蓝牙智能模式下使用 SensorTag。我仅针对 API 18 手机。

public class MainActivity extends ListActivity{

//Bluetooth Management
private BluetoothAdapter btAdapter;
private LeDeviceListAdapter mLeDeviceListAdapter;
private boolean mScanning;

// Set the enable bluetooth code
private final static int REQUEST_ENABLE_BT = 0;


//Handler
private Handler myHandler;
private static final int SCAN_INTERVAL_MS_1 = 10000;

// Notification
private static final int NOTIFY_1 = 0x1001;
private static int mNotificationCount = 1;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    myHandler = new Handler();
    //Check whether BLE is supported on the device(can selectively disable BLE-related features)
    if(!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)){
        Toast.makeText(this, "ble_not_supported", Toast.LENGTH_SHORT).show();
        finish();
    }

    final BluetoothManager btManager =
            (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
    btAdapter = btManager.getAdapter();

    //Check if Bluetooth Adapter is present in the device
    if(btAdapter == null)
    {
        Toast.makeText(this, "Error : Bluetooth not supported", Toast.LENGTH_SHORT).show();
        Log.i("DEBUG_TAG", "No bluetooth available");
        finish();
        return;
    }

}

@Override
public void onStart() {
    super.onStart();
}

@Override
public void onResume() {
    super.onResume();

    // check for Bluetooth enabled on each resume
    if (btAdapter != null && !btAdapter.isEnabled()) {
        Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
    }

    //Initialise list view adapter
    mLeDeviceListAdapter = new LeDeviceListAdapter();
    setListAdapter(mLeDeviceListAdapter);
    scanLeDevice(true);
}


@Override
public void onPause() {
    super.onPause();

    mLeDeviceListAdapter.clear();
    scanLeDevice(true);
    Log.i("DEBUG_TOAST", "Scanning in onPause method!!!");

}

@Override
public void onStop() {
    super.onStop();
    scanLeDevice(false);
    mLeDeviceListAdapter.clear();

}

@Override
public void onRestart() {
    super.onRestart();
    scanLeDevice(true);
}

@Override
public void onDestroy() {
    super.onDestroy();
    btAdapter = null;
}

@Override
protected void onActivityResult(int request_enable_bt, int result_enable_bt, Intent data) {
    if (result_enable_bt == RESULT_OK) {
        //Display "Turn On" message
        Toast.makeText(this, "Turned On", Toast.LENGTH_SHORT).show();
        Log.i("DEBUG_TOAST", "Turn On Toast Success!");

        // Display device name and MAC Address
        BluetoothManager btManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        btAdapter = btManager.getAdapter();
        String address = btAdapter.getAddress();
        String name = btAdapter.getName();
        String toastText = name + " : " + address;
        Toast.makeText(this, toastText, Toast.LENGTH_LONG).show();

        Log.i("DEBUG_TOAST", "Device Name and Address Toast Success!");
    } else if (result_enable_bt == RESULT_CANCELED) {
        Toast.makeText(this, "Didn't Turn On", Toast.LENGTH_SHORT).show();
        Log.i("DEBUG_TOAST", "Turn Off Toast Success!");

        finish();
    }
    super.onActivityResult(request_enable_bt, result_enable_bt, data);
}

@Override
public boolean onCreateOptionsMenu(Menu menu){
    getMenuInflater().inflate(R.menu.main_menu, menu);
    if(!mScanning){
        menu.findItem(R.id.scan).setVisible(true);
        menu.findItem(R.id.stop).setVisible(false);
        menu.findItem(R.id.exit).setVisible(true);
        menu.findItem(R.id.info).setVisible(true);
        menu.findItem(R.id.refresh).setActionView(null);
        Log.i("DEBUG_TOAST", "Menu_mScanning = 0");
    }

    else{
        menu.findItem(R.id.scan).setVisible(false);
        menu.findItem(R.id.stop).setVisible(true);
        menu.findItem(R.id.exit).setVisible(true);
        menu.findItem(R.id.info).setVisible(true);
        menu.findItem(R.id.refresh).setActionView(
                R.layout.actionbar_indeterminate_progress);
        Log.i("DEBUG_TOAST", "Menu_mScanning = 1");
    }
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item){
    switch (item.getItemId()){
        case R.id.scan:

            if(mLeDeviceListAdapter.getCount() >0)
                mLeDeviceListAdapter.clear();
            scanLeDevice(true);
            Log.i("DEBUG_TOAST", "Scan button pressed!");
            break;

        case R.id.stop:
            scanLeDevice(false);
            break;

        case R.id.info:
            Intent information_Intent = new Intent(MainActivity.this, Information.class);
            startActivity(information_Intent);
            break;

        case R.id.reminder:
            Intent reminder_Intent = new Intent(MainActivity.this, Reminder.class);
            startActivity(reminder_Intent);
            break;

        case  R.id.exit:
            finish();
            break;
    }
    return true;
}

protected void onListItemClick(ListView l, View v, int position, long id) {
    final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
    if (device == null) return;
    final Intent intent = new Intent(this, DeviceControlActivity.class);
    intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
    intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
    if (mScanning) {
        btAdapter.stopLeScan(leScanCallback);
        mScanning = false;
    }
    startActivity(intent);
}


private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(final BluetoothDevice device, final int rssi, byte[] scanRecord) {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mLeDeviceListAdapter.addDevice(device, rssi);
                Log.i("DEBUG-ScanCallBack", "add device");
                mLeDeviceListAdapter.notifyDataSetChanged();
                Log.i("DEBUG-ScanCallBack", "notify data set changed");

            }
        });
    }
};


private void scanLeDevice(final boolean enable) {
    if (enable) {
        Log.i("DEBUG_TOAST", "Scanning enabled!");

        // Stops scanning after a pre-defined scan period.
        myHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                mScanning = false;
                Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 0");
                btAdapter.stopLeScan(leScanCallback);
                invalidateOptionsMenu();
            }
        }, SCAN_INTERVAL_MS_1);

        mScanning = true;
        Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 1");

        btAdapter.startLeScan(leScanCallback);

        new CountDownTimer(5000, 1000) {
            public void onFinish() {
                // When timer is finished
                // Execute your code here

                myHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {

                        mLeDeviceListAdapter.clear();
                        Log.i("DEBUG_TOAST", "List Adapter cleared scan handler");

                        mScanning = true;
                        Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 1 (Timer)");
                        btAdapter.startLeScan(leScanCallback);
                        invalidateOptionsMenu();
                        scanLeDevice(true);
                    }
                }, SCAN_INTERVAL_MS_1);  }

            public void onTick(long millisUntilFinished) {
                // millisUntilFinished    The amount of time until finished.
            }
        }.start();

    } else {

        mScanning = false;
        Log.i("DEBUG_TOAST", "scanLeNotEnable_mScanning = 0");

        btAdapter.stopLeScan(leScanCallback);
    }
    invalidateOptionsMenu();
}

//Adapter for holding devices found through scanning
private class LeDeviceListAdapter extends BaseAdapter {
    private ArrayList<BluetoothDevice> mLeDevices;
    private LayoutInflater mInflator;
    private final HashMap<BluetoothDevice, Integer> rssiMap = new HashMap<BluetoothDevice, Integer>();

    public LeDeviceListAdapter() {
        super();
        mLeDevices = new ArrayList<BluetoothDevice>();
        Log.i("LISTVIEW_1", "z" );
        mInflator = MainActivity.this.getLayoutInflater();
        Log.i("LISTVIEW_1", "a" );
    }

    public void addDevice(BluetoothDevice device, int rssi) {
        if (!mLeDevices.contains(device)) {
            mLeDevices.add(device);
            Log.i("LISTVIEW_1", "b");
            rssiMap.put(device, rssi);
            Log.i("LISTVIEW_1", "b1");
        }
        else {
            rssiMap.put(device, rssi);
            Log.i("LISTVIEW_1", "b2");
        }
    }

    public BluetoothDevice getDevice(int position) {
        Log.i("LISTVIEW_1", "c");
        return mLeDevices.get(position);
    }

    public void clear() {
        Log.i("LISTVIEW_1", "d" );
        mLeDevices.clear();
    }

    @Override
    public int getCount() {
        Log.i("LISTVIEW_1", "e" + mLeDevices.size());
        return mLeDevices.size();
    }

    @Override
    public Object getItem(int i) {
        Log.i("LISTVIEW_1", "f" );
        return mLeDevices.get(i);
    }

    @Override
    public long getItemId(int i) {
        Log.i("LISTVIEW_1", "g" );
        return i;
    }

    public View getView(int position, View convertView, ViewGroup parent){
        ViewHolder viewHolder;

        if (convertView == null){
            mInflator =
                    (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
            convertView = mInflator.inflate(R.layout.listitem_device, null);
            Log.i("LISTVIEW_1", "0" );
            viewHolder = new ViewHolder();
            viewHolder.deviceAddress = (TextView) convertView.findViewById(R.id.device_address);
            Log.i("LISTVIEW_1", "1" );
            viewHolder.deviceName = (TextView) convertView.findViewById(R.id.device_name);
            Log.i("LISTVIEW_1", "2" );
            viewHolder.deviceRssi = (TextView) convertView.findViewById(R.id.device_rssi);
            Log.i("LISTVIEW_1", "2.5" );
            convertView.setTag(viewHolder);
            Log.i("LISTVIEW_1", "3");
        }
        else{
            viewHolder = (ViewHolder) convertView.getTag();
            Log.i("LISTVIEW_1", "4" );
        }
        BluetoothDevice device = mLeDevices.get(position);
        Log.i("LISTVIEW_1", "5" );
        final String deviceName = device.getName();
        Log.i("LISTVIEW_1", "6");
        final int deviceRSSI = rssiMap.get(device);
        Log.i("LISTVIEW_1", "6.1");

        if(deviceName != null && deviceName.length() > 0 ) {
            if (deviceRSSI >= -50 && deviceName.equals("CC2650 SensorTag") ||
                    deviceName.equals("SimpleBLEBroadcaster")) {

                viewHolder.deviceName.setText(deviceName);

                NotificationManager notifier = (NotificationManager)
                        getSystemService(Context.NOTIFICATION_SERVICE);

                NotificationCompat.Builder notifyBuilder = new
                        NotificationCompat.Builder(getApplicationContext());

                notifyBuilder.setSmallIcon(R.drawable.hand_sanitise_image);
                notifyBuilder.setTicker("Attention!");
                notifyBuilder.setWhen(System.currentTimeMillis());

                Intent toLaunch = new Intent(MainActivity.this,
                        Reminder.class);
                notifyBuilder.setContentIntent(PendingIntent.getActivity(
                        MainActivity.this, 0, toLaunch, 0));
                notifyBuilder.setContentTitle("Take a look!");
                notifyBuilder.setContentText("Will benefit everyone!!! (" + mNotificationCount++
                        + ")");

                notifyBuilder.setAutoCancel(true);
                notifyBuilder.setVibrate(new long[]{0, 200, 200, 600, 600});
                notifier.notify(NOTIFY_1, notifyBuilder.build());

                Log.i("LISTVIEW_1", "Notification Vibrate Success");



                mScanning = false;
                Log.i("DEBUG_TOAST", "scanLeEnable_mScanning = 0 (Notification)");
                btAdapter.stopLeScan(leScanCallback);
                invalidateOptionsMenu();

                new CountDownTimer(10000, 1000) {
                    public void onFinish() {
                        // When timer is finished
                        // Execute your code here

                        mLeDeviceListAdapter.clear();
                        Log.i("LISTVIEW_1", "List Adapter cleared due to notification");
                        scanLeDevice(true);
                        Log.i("LISTVIEW_1", "CountDownTimer Success!");

                    }

                    public void onTick(long millisUntilFinished) {
                        // millisUntilFinished    The amount of time until finished.
                    }
                }.start();
            }


            viewHolder.deviceName.setText(deviceName);
            Log.i("LISTVIEW_1", "8.1");
            viewHolder.deviceAddress.setText(device.getAddress());
            Log.i("LISTVIEW_1", "9.1");
            viewHolder.deviceRssi.setText("" + rssiMap.get(device) + " dBm");
            Log.i("LISTVIEW_1", "10.1");
        }
        else
            viewHolder.deviceName.setText(R.string.unknown_device);
        Log.i("LISTVIEW_1", "8");
        viewHolder.deviceAddress.setText(device.getAddress());
        Log.i("LISTVIEW_1", "9");
        viewHolder.deviceRssi.setText(""+rssiMap.get(device)+" dBm");
        Log.i("LISTVIEW_1", "10");
        return convertView;
    }

}
static class ViewHolder {
    TextView deviceRssi, deviceAddress, deviceName;
}

}

最佳答案

如果您想在应用程序处于后台时继续扫描 BLE 设备,请使用 Service 。如果您不想让设备休眠,请获取 WakeLock (注意:这会快速耗尽电池电量!)。

关于android - 如何对BLE设备进行后台扫描?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34759328/

相关文章:

android - 获取Android开发TextView最后一行的文字

Android 将 DatePicker 设置为某个日期

linux - 在停用另一个服务之前激活并运行 oneshot 服务

javascript - 使用 JS 或 TS 或 electro 在 OSX 上测试蓝牙状态(事件或非事件)

cordova - 如何根据 tx、rssi 和精度计算到信标的距离

java - 如何调整 tabItem 上的图标大小?

windows - 在 Windows 上重命名正在运行的进程的文件镜像

excel - 如何从 Angular 4 下载 excel 中的数据

java - 如何从 BLE 设备接收数据?

java - 无法解析符号网络 android studio