zoukankan      html  css  js  c++  java
  • 蓝牙介绍

    Android 4.3(API Level 18)介绍了内置平台支持蓝牙低能量的核心作用,并提供了API,应用程序可以用它来发现设备,查询服务,和读写字符。与传统的蓝牙相比,Bluetooth Low Energy (BLE) 旨在提供显著降低功耗。这使得Android应用能够与具有BLE的低耗能设备进行通信,例如,传感器、心率监视器,健身设备,等等。
    
    BLE 权限
        为了在应用程序中使用蓝牙功能,必须声明蓝牙蓝牙许可。您需要这个权限执行任何蓝牙通信,如请求连接,接受连接,传输数据。 
    
        声明蓝牙权限需要在应用的manifest 文件中加如下代码:
    
    <uses-permission android:name="android.permission.BLUETOOTH"/>
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
    
     
    
    如果你想声明应用程序仅BLE-capable设备可用,在你的应用程序的清单包括以下: 
    
    <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
    
    然而,如果你想让你的应用程序可用的设备不支持BLE,你应该还是这个元素包含在您的应用程序的清单,但在运行时设置android:required=“false”。在运行时您可以决定BLE可用性通过使用PackageManager.hasSystemFeature(): 
    
              //用这个检查设备是否支持BLE。
             if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
                         Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
                        finish();
                    }
    
    设置BLE
        BLE在您的应用程序可以交互之前,你需要确认在设备上是支持BLE,如果是这样,确保启用。注意这检查需要设置< uses-feature…/ >为false。
    
        如果不支持BLE,那么你应该禁用任何BLE特性。如果支持,但是已经禁用,你可以用你的应用启动它。完成这个设置需要两步,使用BluetoothAdapter。
    
    1.获取luetoothAdapter。
    
        BluetoothAdapter代表设备的蓝牙适配器。整个系统有一个蓝牙适配器,和您的应用程序可以使用这个对象与它交互。下面的代码片段显示了如何获取适配器。使用getSystemService ()返回一个BluetoothManager实例,然后获取适配器。
    
    Android 4.3(API LEVEL 18)引入了BluetoothManager:
    
    //初始化蓝牙适配器
    final BluetoothManager bluetoothManager =
            (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    mBluetoothAdapter = bluetoothManager.getAdapter();
    
    2.启动蓝牙
    
       接下来,您需要确保启用蓝牙。用isEnabled()检查蓝牙当前是否启动。如果这个方法返回false,那么蓝牙是关闭的。下面的代码片段检查是否启用蓝牙。如果没有,将提示用户去设置启用蓝牙。
    
    
    if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    
     
    
    搜索蓝牙设备
    
      搜索蓝牙设备使用startLeScan()方法。该方法以BluetoothAdapter.LeScanCallback作为参数,您必须实现这个回调,因为这是如何返回扫描结果。因为扫描非常耗电,你应该遵守如下规则:
    
    · 只要找到了设备就应该停止搜索。
    
    · 不要在一个无限循环中搜索, 需要设置一个时间限制搜索. 
    
     
    
    下面代码作用是如何开始和结束搜索:
    
     
    
     
    
    
    public class DeviceScanActivity extends ListActivity {
    
        private BluetoothAdapter mBluetoothAdapter;
        private boolean mScanning;
        private Handler mHandler;
    
        // Stops scanning after 10 seconds.
        private static final long SCAN_PERIOD = 10000;
        ...
        private void scanLeDevice(final boolean enable) {
            if (enable) {
                // Stops scanning after a pre-defined scan period.
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        mScanning = false;
                        mBluetoothAdapter.stopLeScan(mLeScanCallback);
                    }
                }, SCAN_PERIOD);
    
                mScanning = true;
                mBluetoothAdapter.startLeScan(mLeScanCallback);
            } else {
                mScanning = false;
                mBluetoothAdapter.stopLeScan(mLeScanCallback);
            }
            ...
        }
    ...
    }
    
        如果你想只扫描特定类型的外围设备,你可以使用startLeScan(UUID[],BluetoothAdapter.LeScanCallback),提供一个UUID对象数组,指定蓝牙服务应用程序所支持的。 
    
        这里使用BluetoothAdapter.LeScanCallback的实现用来显示蓝牙扫描结果:
    
    private LeDeviceListAdapter mLeDeviceListAdapter;
    ...
    // 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();
               }
           });
       }
    };
    
    注意:不能在同一时间扫描BLE和传统的蓝牙。
    
    链接 GATT Server
          与BLE设备交互的第一步是连接到它,更具体地说,连接到设备上的GATT服务器。连接到GATT服务器使用connectGatt()方法,这个方法取三个参数:一个上下文对象,(布尔指示是否自动连接到设备就可用),和BluetoothGattCallback回调函数。
    
    mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
    
        这个连接到GATT服务端通过BLE设备,并返回一个BluetoothGatt实例,然后您可以使用GATT客户端进行操作。调用者(Android应用程序)是GATT客户端。BluetoothGattCallback用于提供结果给客户端,如连接状态,以及任何进一步的GATT客户端操作。
    
         在这个例子中,有幸获得应用程序提供了一个活动(DeviceControlActivity)连接,显示数据,并显示GATT服务和支持的设备特征。基于用户输入,此活动与一个服务交互称为BluetoothLeService,这服务与BLE设备交互是通过Android BLE API:
    
    public class BluetoothLeService extends Service {
        private final static String TAG = BluetoothLeService.class.getSimpleName();
    
        private BluetoothManager mBluetoothManager;
        private BluetoothAdapter mBluetoothAdapter;
        private String mBluetoothDeviceAddress;
        private BluetoothGatt mBluetoothGatt;
        private int mConnectionState = STATE_DISCONNECTED;
    
        private static final int STATE_DISCONNECTED = 0;
        private static final int STATE_CONNECTING = 1;
        private static final int STATE_CONNECTED = 2;
    
        public final static String ACTION_GATT_CONNECTED =
                "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
        public final static String ACTION_GATT_DISCONNECTED =
                "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
        public final static String ACTION_GATT_SERVICES_DISCOVERED =
                "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
        public final static String ACTION_DATA_AVAILABLE =
                "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
        public final static String EXTRA_DATA =
                "com.example.bluetooth.le.EXTRA_DATA";
    
        public final static UUID UUID_HEART_RATE_MEASUREMENT =
                UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);
    
        // Various callback methods defined by the BLE API.
        private final BluetoothGattCallback mGattCallback =
                new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status,
                    int newState) {
                String intentAction;
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    intentAction = ACTION_GATT_CONNECTED;
                    mConnectionState = STATE_CONNECTED;
                    broadcastUpdate(intentAction);
                    Log.i(TAG, "Connected to GATT server.");
                    Log.i(TAG, "Attempting to start service discovery:" +
                            mBluetoothGatt.discoverServices());
    
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    intentAction = ACTION_GATT_DISCONNECTED;
                    mConnectionState = STATE_DISCONNECTED;
                    Log.i(TAG, "Disconnected from GATT server.");
                    broadcastUpdate(intentAction);
                }
            }
    
            @Override
            // New services discovered
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
                } else {
                    Log.w(TAG, "onServicesDiscovered received: " + status);
                }
            }
    
            @Override
            // Result of a characteristic read operation
            public void onCharacteristicRead(BluetoothGatt gatt,
                    BluetoothGattCharacteristic characteristic,
                    int status) {
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
                }
            }
         ...
        };
    ...
    }
    
     
    
     
    
        当一个特定的回调函数被触发,它调用适当的broadcastUpdate()辅助方法并将其传递一个action。注意,本节中的数据解析执行按照蓝牙心率测量概要文件规范:
    
    private void broadcastUpdate(final String action) {
        final Intent intent = new Intent(action);
        sendBroadcast(intent);
    }
    
    private void broadcastUpdate(final String action,
                                 final BluetoothGattCharacteristic characteristic) {
        final Intent intent = new Intent(action);
    
        // This is special handling for the Heart Rate Measurement profile. Data
        // parsing is carried out as per profile specifications.
        if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
            int flag = characteristic.getProperties();
            int format = -1;
            if ((flag & 0x01) != 0) {
                format = BluetoothGattCharacteristic.FORMAT_UINT16;
                Log.d(TAG, "Heart rate format UINT16.");
            } else {
                format = BluetoothGattCharacteristic.FORMAT_UINT8;
                Log.d(TAG, "Heart rate format UINT8.");
            }
            final int heartRate = characteristic.getIntValue(format, 1);
            Log.d(TAG, String.format("Received heart rate: %d", heartRate));
            intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
        } else {
            // For all other profiles, writes the data formatted in HEX.
            final byte[] data = characteristic.getValue();
            if (data != null && data.length > 0) {
                final StringBuilder stringBuilder = new StringBuilder(data.length);
                for(byte byteChar : data)
                    stringBuilder.append(String.format("%02X ", byteChar));
                intent.putExtra(EXTRA_DATA, new String(data) + "
    " +
                        stringBuilder.toString());
            }
        }
        sendBroadcast(intent);
    }
    
    Back in DeviceControlActivity, these events are handled by a BroadcastReceiver:
    
    // Handles various events fired by the Service.
    // ACTION_GATT_CONNECTED: connected to a GATT server.
    // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
    // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
    // ACTION_DATA_AVAILABLE: received data from the device. This can be a
    // result of read or notification operations.
    private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            final String action = intent.getAction();
            if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
                mConnected = true;
                updateConnectionState(R.string.connected);
                invalidateOptionsMenu();
            } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
                mConnected = false;
                updateConnectionState(R.string.disconnected);
                invalidateOptionsMenu();
                clearUI();
            } else if (BluetoothLeService.
                    ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
                // Show all the supported services and characteristics on the
                // user interface.
                displayGattServices(mBluetoothLeService.getSupportedGattServices());
            } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
                displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
            }
        }
    };
    
     
    
    阅读BLE属性 
    
        一旦你的Android应用程序连接到一个GATT服务器和发现服务,它可以读取和写入属性。例如,这段代码遍历服务器的服务和数据并将它们显示在UI中:
    
    public class DeviceControlActivity extends Activity {
        ...
        // Demonstrates how to iterate through the supported GATT
        // Services/Characteristics.
        // In this sample, we populate the data structure that is bound to the
        // ExpandableListView on the UI.
        private void displayGattServices(List<BluetoothGattService> gattServices) {
            if (gattServices == null) return;
            String uuid = null;
            String unknownServiceString = getResources().
                    getString(R.string.unknown_service);
            String unknownCharaString = getResources().
                    getString(R.string.unknown_characteristic);
            ArrayList<HashMap<String, String>> gattServiceData =
                    new ArrayList<HashMap<String, String>>();
            ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
                    = new ArrayList<ArrayList<HashMap<String, String>>>();
            mGattCharacteristics =
                    new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
    
            // Loops through available GATT Services.
            for (BluetoothGattService gattService : gattServices) {
                HashMap<String, String> currentServiceData =
                        new HashMap<String, String>();
                uuid = gattService.getUuid().toString();
                currentServiceData.put(
                        LIST_NAME, SampleGattAttributes.
                                lookup(uuid, unknownServiceString));
                currentServiceData.put(LIST_UUID, uuid);
                gattServiceData.add(currentServiceData);
    
                ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
                        new ArrayList<HashMap<String, String>>();
                List<BluetoothGattCharacteristic> gattCharacteristics =
                        gattService.getCharacteristics();
                ArrayList<BluetoothGattCharacteristic> charas =
                        new ArrayList<BluetoothGattCharacteristic>();
               // Loops through available Characteristics.
                for (BluetoothGattCharacteristic gattCharacteristic :
                        gattCharacteristics) {
                    charas.add(gattCharacteristic);
                    HashMap<String, String> currentCharaData =
                            new HashMap<String, String>();
                    uuid = gattCharacteristic.getUuid().toString();
                    currentCharaData.put(
                            LIST_NAME, SampleGattAttributes.lookup(uuid,
                                    unknownCharaString));
                    currentCharaData.put(LIST_UUID, uuid);
                    gattCharacteristicGroupData.add(currentCharaData);
                }
                mGattCharacteristics.add(charas);
                gattCharacteristicData.add(gattCharacteristicGroupData);
             }
        ...
        }
    ...
    }
    
    接收GATT通知
       当一个特定的设备上的特征变化需要BLE的应用通知。这个代码片段显示了如何设置一个通知特性,使用setCharacteristicNotification()方法:
    
     
    
     
    
    private BluetoothGatt mBluetoothGatt;
    BluetoothGattCharacteristic characteristic;
    boolean enabled;
    ...
    mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);
    ...
    BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
            UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
    mBluetoothGatt.writeDescriptor(descriptor);
    
         一旦通知作为一个特性被启用,如果远程设备上的特性变化将触发onCharacteristicChanged()回调。
    
     
    
    @Override
    // Characteristic notification
    public void onCharacteristicChanged(BluetoothGatt gatt,
            BluetoothGattCharacteristic characteristic) {
        broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
    }
    
    关闭客户端应用
     
    
        一旦应用程序完成使用BLE设备,应该调用close()方法去释放系统资源。
    
    public void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }
  • 相关阅读:
    ASP.NET 表单验证 Part.1(理解表单验证)
    Silverlight 简介 Part.3(设计 Siverlight 页面)
    ASP.NET 成员资格 Part.3(LoginStatus、LoginView、PasswordRecovery)
    ASP.NET 网站部署 Part.1(安装IIS、复制文件部署网站)
    ASP.NET Dynamic Data Part.1(创建动态数据应用程序)
    ASP.NET 安全模型 Part.2(SSL)
    ASP.NET MVC Part.2(扩展基本的 MVC 应用程序)
    ASP.NET 网站部署 Part.2(使用 Web 部署)
    开发高级 Web 部件
    创建 Web 部件(WebPart 类、简单的 Web 部件)
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/4602519.html
Copyright © 2011-2022 走看看