Android 4.3:如何连接多个低功耗蓝牙设备? [英] Android 4.3: How to connect to multiple Bluetooth Low Energy devices?

查看:41
本文介绍了Android 4.3:如何连接多个低功耗蓝牙设备?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

任务:

我正在开发一个 Android 应用程序,该应用程序需要同时连接到多个(相同,可以通过其 ID 区分)BLE 芯片设备,以便发送和接收更新.我使用了谷歌官方网页上的教程:

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

这帮助我创建了一个 DeviceScanActivity Java 类,它允许我扫描并列出所有可用的 BLE 设备,这与目前大多数此类应用程序所做的类似.我还发现最多 7 个外部从设备可以同时连接到同一个主设备.但是,对我来说,实施这种沟通仍然不清楚.一个有用的链接是:

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:如何连接多个低功耗蓝牙设备

但是,它没有提供足够的细节让我理解这样的实现是如何工作的.

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

我一直在研究这个主题,但找不到任何示例实现.我知道这个问题已经被问过很多次了,但似乎没有任何可行的解决方案/演示可以在线获得让我更清楚.如果有人能向我指出一个资源/工作解决方案,我将非常感激,该解决方案将详细解释修改我现有的 Java 类以实现此功能所需的步骤.

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;
    }
}

当前信息:

所有这些页面都包含一些说明,但它们没有提供关于如何实现多个连接的足够详细信息.

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

UI 目标表示:

我的想法是我的 5.1.1 Android Nexus 7 设备将能够连接到最多 5 个 BLE 芯片从站并发送/接收更新.

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.

推荐答案

我在 Android 中用两台设备完成了这个.首先,根据我的经验,拆分代码要好得多.就我而言,我使用 a 服务进行扫描过程.使用广播接收器,我已将找到的设备对象发送到主要活动.在这个活动中,只完成了我的服务导航的 ui 内容.当我的主要活动接收到蓝牙设备对象时,我将此对象传输到一个单独的服务,其中仅完成蓝牙通信,并使用另一个广播接收器将接收到的数据发送回我的主要活动以显示值.如果您想连接多台设备,您可以像使用一台设备一样使用此模式.在 LeScan Callback 的扫描仪服务中,您可以检查找到的设备的 Macadress.我用我想连接的所有 Mac 地址创建了一个字符串数组.当我找到一个时,我已将其从阵列中删除,当您找到所需的所有设备时,您可以停止整个服务.此外:我的经验是你不应该绑定你的服务.最好手动启动和停止您的服务.当我经常想销毁绑定的服务时,连接仍然保持活动状态,而再次连接时,我的蓝牙行为很奇怪,或者我找不到我的 Ble 服务器设备,因为它在内部仍然是绑定的.

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.

这篇关于Android 4.3:如何连接多个低功耗蓝牙设备?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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