Android 4.3: How to connect to multiple Bluetooth

2020-07-31 01:13发布

问题:

Task:

I am developing an Android application that will need to simultaneously connect to multiple (identical, that can be differentiated via their ID) BLE chip devices in order to send and receive updates. I have used the tutorials on Google's official web page:

http://developer.android.com/guide/topics/connectivity/bluetooth-le.html

This has helped me to create a DeviceScanActivity Java class that allows me to scan for and list all of the available BLE devices in close proximity, similarly to what most applications of this type currently do. I also found out that up to 7 external slaves can be connected to the same master device simultaneously. However, implementing this communication is still unclear for me. A useful link is:

Android 4.3: How to connect to multiple Bluetooth Low Energy devices

However, it does not provide enough detail for me to understand how such an implementation works.

I have been researching this topic for a while and was not able to find any example implementations. I am aware that this question has been asked a lot of times but there does not seem to be any working solution/ demo that is available online to make things more clear for me. I will appreciate it a lot if anyone can point me to a resource/ working solution that will explain in detail the steps that are necessary to modify my existing Java class in order to implement this functionality.

DeviceScanActivity.java:

import android.app.Activity;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

/**
 * Activity for scanning and displaying available Bluetooth LE devices.
 */
public class DeviceScanActivity extends ListActivity {
    private LeDeviceListAdapter mLeDeviceListAdapter;
    private BluetoothAdapter mBluetoothAdapter;
    private boolean mScanning;
    private Handler mHandler;

    private static final int REQUEST_ENABLE_BT = 1;
    // Stops scanning after 10 seconds.
    private static final long SCAN_PERIOD = 10000;

    private static final String CONFIG_FILE = "file";

    private int _countClick = 0;
    private boolean experimenterMode = false;
    private String pairedDeviceAddress = "";



    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getActionBar().setTitle(R.string.title_devices);
        mHandler = new Handler();

        // Use this check to determine whether BLE is supported on the device.  Then you can
        // selectively disable BLE-related features.
        if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
            finish();
        }

        // Initializes a Bluetooth adapter.  For API level 18 and above, get a reference to
        // BluetoothAdapter through BluetoothManager.
        final BluetoothManager bluetoothManager =
                (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
        mBluetoothAdapter = bluetoothManager.getAdapter();

        // Checks if Bluetooth is supported on the device.
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, R.string.error_bluetooth_not_supported, Toast.LENGTH_SHORT).show();
            finish();
            return;
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.main, menu);
        if (!mScanning) {
            menu.findItem(R.id.menu_stop).setVisible(false);
            menu.findItem(R.id.menu_scan).setVisible(true);
            menu.findItem(R.id.menu_refresh).setActionView(null);
        } else {
            menu.findItem(R.id.menu_stop).setVisible(true);
            menu.findItem(R.id.menu_scan).setVisible(false);
            menu.findItem(R.id.menu_refresh).setActionView(
                    R.layout.actionbar_indeterminate_progress);
        }
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.menu_scan:
                mLeDeviceListAdapter.clear();
                scanLeDevice(true);
                break;
            case R.id.menu_stop:
                scanLeDevice(false);
                break;
            case android.R.id.home:
                //onBackPressed();
                _countClick++;
                if(_countClick>8)
                {
                    toggleExperimenterMode();
                }
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

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

        // Ensures Bluetooth is enabled on the device.  If Bluetooth is not currently enabled,
        // fire an intent to display a dialog asking the user to grant permission to enable it.
        if (!mBluetoothAdapter.isEnabled()) {
            if (!mBluetoothAdapter.isEnabled()) {
                Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
            }
        }

        // Restore preferences
        SharedPreferences settings = getSharedPreferences(CONFIG_FILE, 0);
        experimenterMode = settings.getBoolean("experimenterMode", false);
        pairedDeviceAddress = settings.getString("pairedDeviceAddress", "");

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

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // User chose not to enable Bluetooth.
        if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
            finish();
            return;
        }
        super.onActivityResult(requestCode, resultCode, data);
    }

    @Override
    protected void onPause() {
        super.onPause();
        scanLeDevice(false);
        mLeDeviceListAdapter.clear();
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
        if (device == null) return;
        connectToDevice(device);
    }



    private void connectToDevice(BluetoothDevice device){
        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) {
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
            mScanning = false;
        }

        SharedPreferences settings = getSharedPreferences(CONFIG_FILE, 0);
        SharedPreferences.Editor editor = settings.edit();
        editor.putString("pairedDeviceAddress", device.getAddress());
        editor.commit();

        startActivity(intent);
    }

    //---------------------------------------------------

    private void scanLeDevice(final boolean enable) {
        ProgressDialog progress;
        if (enable) {
            // Stops scanning after a pre-defined scan period.
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    mScanning = false;
                    mBluetoothAdapter.stopLeScan(mLeScanCallback);
                    invalidateOptionsMenu();
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);

        }
        invalidateOptionsMenu();
    }

    private void toggleExperimenterMode(){
        _countClick=0;
        experimenterMode = !experimenterMode;
        SharedPreferences settings = getSharedPreferences(CONFIG_FILE, 0);
        SharedPreferences.Editor editor = settings.edit();
        editor.putBoolean("experimenterMode", experimenterMode);
        editor.commit();
        String t = experimenterMode ? "You are now in Experimenter mode":"You are now in User mode";
        Toast.makeText(this, t, Toast.LENGTH_SHORT).show();
    }

    // Adapter for holding devices found through scanning.
    private class LeDeviceListAdapter extends BaseAdapter {
        private ArrayList<BluetoothDevice> mLeDevices;
        private LayoutInflater mInflator;

        public LeDeviceListAdapter() {
            super();
            mLeDevices = new ArrayList<BluetoothDevice>();
            mInflator = DeviceScanActivity.this.getLayoutInflater();
        }

        public void addDevice(BluetoothDevice device) {
            if(!mLeDevices.contains(device)) {
                mLeDevices.add(device);
            }
            if (!experimenterMode && ! pairedDeviceAddress.isEmpty()) {
                if (device.getAddress().equals(pairedDeviceAddress)) {
                    connectToDevice(device);
                }
            }
        }

        public BluetoothDevice getDevice(int position) {
            return mLeDevices.get(position);
        }

        public void clear() {
            mLeDevices.clear();
        }

        @Override
        public int getCount() {
            return mLeDevices.size();
        }

        @Override
        public Object getItem(int i) {
            return mLeDevices.get(i);
        }

        @Override
        public long getItemId(int i) {
            return i;
        }

        @Override
        public View getView(int i, View view, ViewGroup viewGroup) {
            ViewHolder viewHolder;
            // General ListView optimization code.
            if (view == null) {
                view = mInflator.inflate(R.layout.listitem_device, null);
                viewHolder = new ViewHolder();
                viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
                viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
                view.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder) view.getTag();
            }

            BluetoothDevice device = mLeDevices.get(i);
            final String deviceName = device.getName();
            if (deviceName != null && deviceName.length() > 0)
                viewHolder.deviceName.setText(deviceName);
            else
                viewHolder.deviceName.setText(R.string.unknown_device);
            viewHolder.deviceAddress.setText(device.getAddress());

            return view;
        }
    }

    // Device scan callback.
    private BluetoothAdapter.LeScanCallback mLeScanCallback =
            new BluetoothAdapter.LeScanCallback() {

        @Override
        public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    mLeDeviceListAdapter.addDevice(device);
                    mLeDeviceListAdapter.notifyDataSetChanged();
                }
            });
        }
    };

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

Current Information:

  • Android 4.3: How to connect to multiple Bluetooth Low Energy devices
  • https://developer.android.com/guide/topics/connectivity/bluetooth-le.html
  • Does Android 4.3 support multiple BLE device connections?
  • Ble multiple connection
  • https://labs.hybris.com/2014/10/06/connecting-to-multiple-ble-devices/
  • https://e2e.ti.com/support/wireless_connectivity/f/538/t/225935

All of these pages contain some directions, but they do not provide enough detail on how multiple connections can be implemented.

UI Target Representation:

The idea is that my 5.1.1 Android Nexus 7 device will be able to connect to at most 5 BLE chip slaves and send/receive updates.

回答1:

I've done this in Android with two devices. First in my experience it is much better to split your code. In my case, I used the a service for the scanning process. With a broadcastreceiver I've sent the found device Object to ma main activity. In this Activity in genereal only the ui stuff an my Servicenavigation is done. When my main activity revceived the Bluetoothdevice Object I transmit this Object to a seperate Service where only the Bluetoothcommunication is done and with another Broadcastreceiver I sent the received Data back to my mainactivity to display values. If you want to connect multiple devices, you can use this pattern the same way as you would do with one device. In the scanner service in you LeScan Callback you check the found devices for their Macadress. I've mad a String Array with all the Mac Adresses I want to connect to. When I found one I've deleted it from the Array and when you've found all the devices you want you can stop the whole service. In Addition: I've made the experience that you should not bind you service. it is better to start and stop your service manually. When I wanted to destroy my bound service pretty often the connection stayed alive an when connecting again my Bluetooth behaves weird or I can't find my Ble server device because internally it is still bound.



回答2:

I had the same problem for my app, documentation about BLE on android is very little, but after a lot of research I created a well working bluetooth library for connection between multiple devices: https://github.com/niedev/RTranslator/tree/master/app/src/main/java/nie/translator/rtranslatordevedition/voice_translation/_conversation_mode/communication/communicator

p.s. In my tests of the library the max number of stable connected devices is 4 (even with reconnection)

Edit: The moderators continue to cancel my answer because I use a link to my code instead of insert it directly here but I can't because the code is nearly 4600 lines of code splitted in 10 classes (and the number of characters in an answer is limited)