TShopping

 找回密碼
 註冊
搜索
查看: 427|回復: 0

[教學] Android BLE藍牙開發 詳解

[複製鏈接]
發表於 2020-4-5 12:12:19 | 顯示全部樓層 |閱讀模式
 
Push to Facebook Push to Plurk  
演示

Android 藍牙開發 BLE

Android 藍牙開發 BLE


聲明
本文主要講解BLE(低功耗藍牙4.0以上)的使用和封裝,為了UI層方便拿取數據展示,統一對藍牙搜索、連接、數據交互、藍牙協議等封裝為lib。

一.BLE簡介
為什麼要學習藍牙技術,藍牙作為一種成熟、低功耗無線通信技術的先鋒,在可穿戴設備領域中扮演著越來越重要的作用。

BLE分為三部分:Service,Characteristic,Descriptor。這三部分都是使用UUID來作為唯一標識符加以區分。一個BLE終端可以包含多個Service,一個Service可以包含多個Characteristic,而一個Characteristic包含一個value和多個Descriptor,一個Descriptor只包含一個value。UUID格式為:0000ffe1-0000-1000-8000-00805f9b34fb

二.藍牙前期簡單介紹
1.添加權限
  1. <uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>
  2.     <uses-permission android:name="android.permission.BLUETOOTH"/><!--藍牙管理-->
  3.     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/><!--藍牙操作權限-->
  4.     <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
  5. <application ....>
  6. <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" /><!--表示此app只支持擁有BLE的設備上運行-->
  7. </application>
複製代碼
如果未加此權限會出現
  1. <uses-feature android:name="android.hardware.bluetooth_le" android:required="true" />
複製代碼

Attempt to invoke virtual methodmBluetoothLeService on a null object reference
如果你不確定你的app使用的設備是否支持低功耗藍牙,但又想讓支持的設備使用低功耗藍牙,把標誌位改為false,同時去代碼中判斷設備是否支持BLE:

  1. // 位置選項已經打開了,檢查手機有沒有支援BLE
  2.                 Log.d(TAG, "位置選項已經打開了,檢查手機有沒有支援BLE");
  3.                 if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
  4.                     Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
  5.                     hasBlePermission = false;
  6.                 }}
複製代碼

2.藍牙適配器BluetoothAdapter獲取方式:

  1. BluetoothManager bluetoothManager =(BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
  2. BluetoothAdapter mBluetoothAdapter = bluetoothManager.getAdapter();
複製代碼

此處需要做非空判斷,為null則表示設備不支持藍牙。

BluetoothAdapter方法描述:
isEnabled() 判斷系統藍牙是否打開
disable() 無彈窗提示關閉系統藍牙
enable() 無彈窗提示打開系統藍牙(此操作有點不友好!)
startDiscovery() 開始搜索設備—–適合經典藍牙和低功耗藍牙兩種
cancelDiscovery() 取消搜索設備
startLeScan() 開始搜索設備—–適合掃描低功耗藍牙,但是在api21以上被標記廢棄使用
stopLeScan() 停止搜索設備
startScan() 開始搜索設備—–api21以上掃描低功耗藍牙,通過bluetoothAdapter.getBluetoothLeScanner()方法獲取
stopScan() 停止搜索設備
stopScan() 停止搜索設備
說明:startDiscovery()方法在大多數手機上是可以同時發現經典藍牙和低功耗藍牙(BLE)的,但是startDiscovery()的回調無法返回BLE的廣播,所以無法通過廣播識別設備,而且startDiscovery()掃描BLE效率比startLeScan()低很多。因此需要根據具體的需求去做適配,才能更高效的搜尋藍牙。PS: startLeScan()和startScan()有重載方法可以指定規則,參數去搜索。

3.藍牙協議BluetoothGatt
獲取方式:

  1. BluetoothGatt mBluetoothGatt = device.connectGatt(getContext(), false, mGattCallback);//创建连接
複製代碼

BluetoothGatt方法描述:
connect()  連接遠程設備
discoverServices()  搜索連接設備所支持的service
disconnect()  斷開與遠程設備的GATT連接
close() 關閉GATT Client端,釋放資源
readCharacteristic(characteristic) 在指定的characteristic特徵端口中讀取數據
writeDescriptor(characteristic) 在指定的characteristic特徵端口中寫入數據
setCharacteristicNotification(characteristic, enabled) 設置當指定characteristic特徵端口值變化時,是否發出通知返回
readRemoteRssi() 讀取當前連接設備信號值,返回數值為負,值越小信號越弱。
getServices() 獲取遠程設備所支持的services
getDevice() 獲取已連接的設備信息


與BLE藍牙交互分為三步驟:搜索、連接、讀寫數據。BLE藍牙無需配對即可連接
源碼下載

搜索
  1. package com.example.android.bluetoothlegatt;

  2. import android.app.Activity;
  3. import android.app.ListActivity;
  4. import android.bluetooth.BluetoothAdapter;
  5. import android.bluetooth.BluetoothDevice;
  6. import android.bluetooth.BluetoothManager;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.pm.PackageManager;
  10. import android.os.Bundle;
  11. import android.os.Handler;
  12. import android.view.LayoutInflater;
  13. import android.view.Menu;
  14. import android.view.MenuItem;
  15. import android.view.View;
  16. import android.view.ViewGroup;
  17. import android.widget.BaseAdapter;
  18. import android.widget.ListView;
  19. import android.widget.TextView;
  20. import android.widget.Toast;

  21. import java.util.ArrayList;

  22. /**
  23. * Activity for scanning and displaying available Bluetooth LE devices.
  24. */
  25. public class DeviceScanActivity extends ListActivity {
  26.     private LeDeviceListAdapter mLeDeviceListAdapter;
  27.     private BluetoothAdapter mBluetoothAdapter;
  28.     private boolean mScanning;
  29.     private Handler mHandler;

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

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

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

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

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

  56.     @Override
  57.     public boolean onCreateOptionsMenu(Menu menu) {
  58.         getMenuInflater().inflate(R.menu.main, menu);
  59.         if (!mScanning) {
  60.             menu.findItem(R.id.menu_stop).setVisible(false);
  61.             menu.findItem(R.id.menu_scan).setVisible(true);
  62.             menu.findItem(R.id.menu_refresh).setActionView(null);
  63.         } else {
  64.             menu.findItem(R.id.menu_stop).setVisible(true);
  65.             menu.findItem(R.id.menu_scan).setVisible(false);
  66.             menu.findItem(R.id.menu_refresh).setActionView(
  67.                     R.layout.actionbar_indeterminate_progress);
  68.         }
  69.         return true;
  70.     }

  71.     @Override
  72.     public boolean onOptionsItemSelected(MenuItem item) {
  73.         switch (item.getItemId()) {
  74.             case R.id.menu_scan:
  75.                 mLeDeviceListAdapter.clear();
  76.                 scanLeDevice(true);
  77.                 break;
  78.             case R.id.menu_stop:
  79.                 scanLeDevice(false);
  80.                 break;
  81.         }
  82.         return true;
  83.     }

  84.     @Override
  85.     protected void onResume() {
  86.         super.onResume();

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

  95.         // Initializes list view adapter.
  96.         mLeDeviceListAdapter = new LeDeviceListAdapter();
  97.         setListAdapter(mLeDeviceListAdapter);
  98.         scanLeDevice(true);
  99.     }

  100.     @Override
  101.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  102.         // User chose not to enable Bluetooth.
  103.         if (requestCode == REQUEST_ENABLE_BT && resultCode == Activity.RESULT_CANCELED) {
  104.             finish();
  105.             return;
  106.         }
  107.         super.onActivityResult(requestCode, resultCode, data);
  108.     }

  109.     @Override
  110.     protected void onPause() {
  111.         super.onPause();
  112.         scanLeDevice(false);
  113.         mLeDeviceListAdapter.clear();
  114.     }

  115.     @Override
  116.     protected void onListItemClick(ListView l, View v, int position, long id) {
  117.         final BluetoothDevice device = mLeDeviceListAdapter.getDevice(position);
  118.         if (device == null) return;
  119.         final Intent intent = new Intent(this, DeviceControlActivity.class);
  120.         intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_NAME, device.getName());
  121.         intent.putExtra(DeviceControlActivity.EXTRAS_DEVICE_ADDRESS, device.getAddress());
  122.         if (mScanning) {
  123.             mBluetoothAdapter.stopLeScan(mLeScanCallback);
  124.             mScanning = false;
  125.         }
  126.         startActivity(intent);
  127.     }

  128.     private void scanLeDevice(final boolean enable) {
  129.         if (enable) {
  130.             // Stops scanning after a pre-defined scan period.
  131.             mHandler.postDelayed(new Runnable() {
  132.                 @Override
  133.                 public void run() {
  134.                     mScanning = false;
  135.                     mBluetoothAdapter.stopLeScan(mLeScanCallback);
  136.                     invalidateOptionsMenu();
  137.                 }
  138.             }, SCAN_PERIOD);

  139.             mScanning = true;
  140.             mBluetoothAdapter.startLeScan(mLeScanCallback);
  141.         } else {
  142.             mScanning = false;
  143.             mBluetoothAdapter.stopLeScan(mLeScanCallback);
  144.         }
  145.         invalidateOptionsMenu();
  146.     }

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

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

  156.         public void addDevice(BluetoothDevice device) {
  157.             if(!mLeDevices.contains(device)) {
  158.                 mLeDevices.add(device);
  159.             }
  160.         }

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

  164.         public void clear() {
  165.             mLeDevices.clear();
  166.         }

  167.         @Override
  168.         public int getCount() {
  169.             return mLeDevices.size();
  170.         }

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

  175.         @Override
  176.         public long getItemId(int i) {
  177.             return i;
  178.         }

  179.         @Override
  180.         public View getView(int i, View view, ViewGroup viewGroup) {
  181.             ViewHolder viewHolder;
  182.             // General ListView optimization code.
  183.             if (view == null) {
  184.                 view = mInflator.inflate(R.layout.listitem_device, null);
  185.                 viewHolder = new ViewHolder();
  186.                 viewHolder.deviceAddress = (TextView) view.findViewById(R.id.device_address);
  187.                 viewHolder.deviceName = (TextView) view.findViewById(R.id.device_name);
  188.                 view.setTag(viewHolder);
  189.             } else {
  190.                 viewHolder = (ViewHolder) view.getTag();
  191.             }

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

  199.             return view;
  200.         }
  201.     }

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

  205.         @Override
  206.         public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
  207.             runOnUiThread(new Runnable() {
  208.                 @Override
  209.                 public void run() {
  210.                     mLeDeviceListAdapter.addDevice(device);
  211.                     mLeDeviceListAdapter.notifyDataSetChanged();
  212.                 }
  213.             });
  214.         }
  215.     };

  216.     static class ViewHolder {
  217.         TextView deviceName;
  218.         TextView deviceAddress;
  219.     }
  220. }
複製代碼


連接源碼
  1. package com.example.android.bluetoothlegatt;

  2. import android.app.Activity;
  3. import android.bluetooth.BluetoothGattCharacteristic;
  4. import android.bluetooth.BluetoothGattService;
  5. import android.content.BroadcastReceiver;
  6. import android.content.ComponentName;
  7. import android.content.Context;
  8. import android.content.Intent;
  9. import android.content.IntentFilter;
  10. import android.content.ServiceConnection;
  11. import android.os.Bundle;
  12. import android.os.IBinder;
  13. import android.util.Log;
  14. import android.view.Menu;
  15. import android.view.MenuItem;
  16. import android.view.View;
  17. import android.widget.ExpandableListView;
  18. import android.widget.SimpleExpandableListAdapter;
  19. import android.widget.TextView;

  20. import java.util.ArrayList;
  21. import java.util.HashMap;
  22. import java.util.List;

  23. /**
  24. * For a given BLE device, this Activity provides the user interface to connect, display data,
  25. * and display GATT services and characteristics supported by the device.  The Activity
  26. * communicates with {@code BluetoothLeService}, which in turn interacts with the
  27. * Bluetooth LE API.
  28. */
  29. public class DeviceControlActivity extends Activity {
  30.     private final static String TAG = DeviceControlActivity.class.getSimpleName();

  31.     public static final String EXTRAS_DEVICE_NAME = "DEVICE_NAME";
  32.     public static final String EXTRAS_DEVICE_ADDRESS = "DEVICE_ADDRESS";

  33.     private TextView mConnectionState;
  34.     private TextView mDataField;
  35.     private String mDeviceName;
  36.     private String mDeviceAddress;
  37.     private ExpandableListView mGattServicesList;
  38.     private BluetoothLeService mBluetoothLeService;
  39.     private ArrayList<ArrayList<BluetoothGattCharacteristic>> mGattCharacteristics =
  40.             new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
  41.     private boolean mConnected = false;
  42.     private BluetoothGattCharacteristic mNotifyCharacteristic;

  43.     private final String LIST_NAME = "NAME";
  44.     private final String LIST_UUID = "UUID";

  45.     // Code to manage Service lifecycle.
  46.     private final ServiceConnection mServiceConnection = new ServiceConnection() {

  47.         @Override
  48.         public void onServiceConnected(ComponentName componentName, IBinder service) {
  49.             mBluetoothLeService = ((BluetoothLeService.LocalBinder) service).getService();
  50.             if (!mBluetoothLeService.initialize()) {
  51.                 Log.e(TAG, "Unable to initialize Bluetooth");
  52.                 finish();
  53.             }
  54.             // Automatically connects to the device upon successful start-up initialization.
  55.             mBluetoothLeService.connect(mDeviceAddress);
  56.         }

  57.         @Override
  58.         public void onServiceDisconnected(ComponentName componentName) {
  59.             mBluetoothLeService = null;
  60.         }
  61.     };

  62.     // Handles various events fired by the Service.
  63.     // ACTION_GATT_CONNECTED: connected to a GATT server.
  64.     // ACTION_GATT_DISCONNECTED: disconnected from a GATT server.
  65.     // ACTION_GATT_SERVICES_DISCOVERED: discovered GATT services.
  66.     // ACTION_DATA_AVAILABLE: received data from the device.  This can be a result of read
  67.     //                        or notification operations.
  68.     private final BroadcastReceiver mGattUpdateReceiver = new BroadcastReceiver() {
  69.         @Override
  70.         public void onReceive(Context context, Intent intent) {
  71.             final String action = intent.getAction();
  72.             if (BluetoothLeService.ACTION_GATT_CONNECTED.equals(action)) {
  73.                 mConnected = true;
  74.                 updateConnectionState(R.string.connected);
  75.                 invalidateOptionsMenu();
  76.             } else if (BluetoothLeService.ACTION_GATT_DISCONNECTED.equals(action)) {
  77.                 mConnected = false;
  78.                 updateConnectionState(R.string.disconnected);
  79.                 invalidateOptionsMenu();
  80.                 clearUI();
  81.             } else if (BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED.equals(action)) {
  82.                 // Show all the supported services and characteristics on the user interface.
  83.                 displayGattServices(mBluetoothLeService.getSupportedGattServices());
  84.             } else if (BluetoothLeService.ACTION_DATA_AVAILABLE.equals(action)) {
  85.                 displayData(intent.getStringExtra(BluetoothLeService.EXTRA_DATA));
  86.             }
  87.         }
  88.     };

  89.     // If a given GATT characteristic is selected, check for supported features.  This sample
  90.     // demonstrates 'Read' and 'Notify' features.  See
  91.     // http://d.android.com/reference/android/bluetooth/BluetoothGatt.html for the complete
  92.     // list of supported characteristic features.
  93.     private final ExpandableListView.OnChildClickListener servicesListClickListner =
  94.             new ExpandableListView.OnChildClickListener() {
  95.                 @Override
  96.                 public boolean onChildClick(ExpandableListView parent, View v, int groupPosition,
  97.                                             int childPosition, long id) {
  98.                     if (mGattCharacteristics != null) {
  99.                         final BluetoothGattCharacteristic characteristic =
  100.                                 mGattCharacteristics.get(groupPosition).get(childPosition);
  101.                         final int charaProp = characteristic.getProperties();
  102.                         if ((charaProp | BluetoothGattCharacteristic.PROPERTY_READ) > 0) {
  103.                             // If there is an active notification on a characteristic, clear
  104.                             // it first so it doesn't update the data field on the user interface.
  105.                             if (mNotifyCharacteristic != null) {
  106.                                 mBluetoothLeService.setCharacteristicNotification(
  107.                                         mNotifyCharacteristic, false);
  108.                                 mNotifyCharacteristic = null;
  109.                             }
  110.                             mBluetoothLeService.readCharacteristic(characteristic);
  111.                         }
  112.                         if ((charaProp | BluetoothGattCharacteristic.PROPERTY_NOTIFY) > 0) {
  113.                             mNotifyCharacteristic = characteristic;
  114.                             mBluetoothLeService.setCharacteristicNotification(
  115.                                     characteristic, true);
  116.                         }
  117.                         return true;
  118.                     }
  119.                     return false;
  120.                 }
  121.     };

  122.     private void clearUI() {
  123.         mGattServicesList.setAdapter((SimpleExpandableListAdapter) null);
  124.         mDataField.setText(R.string.no_data);
  125.     }

  126.     @Override
  127.     public void onCreate(Bundle savedInstanceState) {
  128.         super.onCreate(savedInstanceState);
  129.         setContentView(R.layout.gatt_services_characteristics);

  130.         final Intent intent = getIntent();
  131.         mDeviceName = intent.getStringExtra(EXTRAS_DEVICE_NAME);
  132.         mDeviceAddress = intent.getStringExtra(EXTRAS_DEVICE_ADDRESS);

  133.         // Sets up UI references.
  134.         ((TextView) findViewById(R.id.device_address)).setText(mDeviceAddress);
  135.         mGattServicesList = (ExpandableListView) findViewById(R.id.gatt_services_list);
  136.         mGattServicesList.setOnChildClickListener(servicesListClickListner);
  137.         mConnectionState = (TextView) findViewById(R.id.connection_state);
  138.         mDataField = (TextView) findViewById(R.id.data_value);

  139.         getActionBar().setTitle(mDeviceName);
  140.         getActionBar().setDisplayHomeAsUpEnabled(true);
  141.         Intent gattServiceIntent = new Intent(this, BluetoothLeService.class);
  142.         bindService(gattServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
  143.     }

  144.     @Override
  145.     protected void onResume() {
  146.         super.onResume();
  147.         registerReceiver(mGattUpdateReceiver, makeGattUpdateIntentFilter());
  148.         if (mBluetoothLeService != null) {
  149.             final boolean result = mBluetoothLeService.connect(mDeviceAddress);
  150.             Log.d(TAG, "Connect request result=" + result);
  151.         }
  152.     }

  153.     @Override
  154.     protected void onPause() {
  155.         super.onPause();
  156.         unregisterReceiver(mGattUpdateReceiver);
  157.     }

  158.     @Override
  159.     protected void onDestroy() {
  160.         super.onDestroy();
  161.         unbindService(mServiceConnection);
  162.         mBluetoothLeService = null;
  163.     }

  164.     @Override
  165.     public boolean onCreateOptionsMenu(Menu menu) {
  166.         getMenuInflater().inflate(R.menu.gatt_services, menu);
  167.         if (mConnected) {
  168.             menu.findItem(R.id.menu_connect).setVisible(false);
  169.             menu.findItem(R.id.menu_disconnect).setVisible(true);
  170.         } else {
  171.             menu.findItem(R.id.menu_connect).setVisible(true);
  172.             menu.findItem(R.id.menu_disconnect).setVisible(false);
  173.         }
  174.         return true;
  175.     }

  176.     @Override
  177.     public boolean onOptionsItemSelected(MenuItem item) {
  178.         switch(item.getItemId()) {
  179.             case R.id.menu_connect:
  180.                 mBluetoothLeService.connect(mDeviceAddress);
  181.                 return true;
  182.             case R.id.menu_disconnect:
  183.                 mBluetoothLeService.disconnect();
  184.                 return true;
  185.             case android.R.id.home:
  186.                 onBackPressed();
  187.                 return true;
  188.         }
  189.         return super.onOptionsItemSelected(item);
  190.     }

  191.     private void updateConnectionState(final int resourceId) {
  192.         runOnUiThread(new Runnable() {
  193.             @Override
  194.             public void run() {
  195.                 mConnectionState.setText(resourceId);
  196.             }
  197.         });
  198.     }

  199.     private void displayData(String data) {
  200.         if (data != null) {
  201.             mDataField.setText(data);
  202.         }
  203.     }

  204.     // Demonstrates how to iterate through the supported GATT Services/Characteristics.
  205.     // In this sample, we populate the data structure that is bound to the ExpandableListView
  206.     // on the UI.
  207.     private void displayGattServices(List<BluetoothGattService> gattServices) {
  208.         if (gattServices == null) return;
  209.         String uuid = null;
  210.         String unknownServiceString = getResources().getString(R.string.unknown_service);
  211.         String unknownCharaString = getResources().getString(R.string.unknown_characteristic);
  212.         ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
  213.         ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData
  214.                 = new ArrayList<ArrayList<HashMap<String, String>>>();
  215.         mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();

  216.         // Loops through available GATT Services.
  217.         for (BluetoothGattService gattService : gattServices) {
  218.             HashMap<String, String> currentServiceData = new HashMap<String, String>();
  219.             uuid = gattService.getUuid().toString();
  220.             currentServiceData.put(
  221.                     LIST_NAME, SampleGattAttributes.lookup(uuid, unknownServiceString));
  222.             currentServiceData.put(LIST_UUID, uuid);
  223.             gattServiceData.add(currentServiceData);

  224.             ArrayList<HashMap<String, String>> gattCharacteristicGroupData =
  225.                     new ArrayList<HashMap<String, String>>();
  226.             List<BluetoothGattCharacteristic> gattCharacteristics =
  227.                     gattService.getCharacteristics();
  228.             ArrayList<BluetoothGattCharacteristic> charas =
  229.                     new ArrayList<BluetoothGattCharacteristic>();

  230.             // Loops through available Characteristics.
  231.             for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
  232.                 charas.add(gattCharacteristic);
  233.                 HashMap<String, String> currentCharaData = new HashMap<String, String>();
  234.                 uuid = gattCharacteristic.getUuid().toString();
  235.                 currentCharaData.put(
  236.                         LIST_NAME, SampleGattAttributes.lookup(uuid, unknownCharaString));
  237.                 currentCharaData.put(LIST_UUID, uuid);
  238.                 gattCharacteristicGroupData.add(currentCharaData);
  239.             }
  240.             mGattCharacteristics.add(charas);
  241.             gattCharacteristicData.add(gattCharacteristicGroupData);
  242.         }

  243.         SimpleExpandableListAdapter gattServiceAdapter = new SimpleExpandableListAdapter(
  244.                 this,
  245.                 gattServiceData,
  246.                 android.R.layout.simple_expandable_list_item_2,
  247.                 new String[] {LIST_NAME, LIST_UUID},
  248.                 new int[] { android.R.id.text1, android.R.id.text2 },
  249.                 gattCharacteristicData,
  250.                 android.R.layout.simple_expandable_list_item_2,
  251.                 new String[] {LIST_NAME, LIST_UUID},
  252.                 new int[] { android.R.id.text1, android.R.id.text2 }
  253.         );
  254.         mGattServicesList.setAdapter(gattServiceAdapter);
  255.     }

  256.     private static IntentFilter makeGattUpdateIntentFilter() {
  257.         final IntentFilter intentFilter = new IntentFilter();
  258.         intentFilter.addAction(BluetoothLeService.ACTION_GATT_CONNECTED);
  259.         intentFilter.addAction(BluetoothLeService.ACTION_GATT_DISCONNECTED);
  260.         intentFilter.addAction(BluetoothLeService.ACTION_GATT_SERVICES_DISCOVERED);
  261.         intentFilter.addAction(BluetoothLeService.ACTION_DATA_AVAILABLE);
  262.         return intentFilter;
  263.     }
  264. }
複製代碼


連接程式碼
  1. // Automatically connects to the device upon successful start-up initialization.
  2.             mBluetoothLeService.connect(mDeviceAddress);
複製代碼


BluetoothLeService.java
  1. package com.example.android.bluetoothlegatt;

  2. import android.app.Service;
  3. import android.bluetooth.BluetoothAdapter;
  4. import android.bluetooth.BluetoothDevice;
  5. import android.bluetooth.BluetoothGatt;
  6. import android.bluetooth.BluetoothGattCallback;
  7. import android.bluetooth.BluetoothGattCharacteristic;
  8. import android.bluetooth.BluetoothGattDescriptor;
  9. import android.bluetooth.BluetoothGattService;
  10. import android.bluetooth.BluetoothManager;
  11. import android.bluetooth.BluetoothProfile;
  12. import android.content.Context;
  13. import android.content.Intent;
  14. import android.os.Binder;
  15. import android.os.IBinder;
  16. import android.util.Log;

  17. import java.util.List;
  18. import java.util.UUID;

  19. /**
  20. * Service for managing connection and data communication with a GATT server hosted on a
  21. * given Bluetooth LE device.
  22. */
  23. public class BluetoothLeService extends Service {
  24.     private final static String TAG = BluetoothLeService.class.getSimpleName();

  25.     private BluetoothManager mBluetoothManager;
  26.     private BluetoothAdapter mBluetoothAdapter;
  27.     private String mBluetoothDeviceAddress;
  28.     private BluetoothGatt mBluetoothGatt;
  29.     private int mConnectionState = STATE_DISCONNECTED;

  30.     private static final int STATE_DISCONNECTED = 0;
  31.     private static final int STATE_CONNECTING = 1;
  32.     private static final int STATE_CONNECTED = 2;

  33.     public final static String ACTION_GATT_CONNECTED =
  34.             "com.example.bluetooth.le.ACTION_GATT_CONNECTED";
  35.     public final static String ACTION_GATT_DISCONNECTED =
  36.             "com.example.bluetooth.le.ACTION_GATT_DISCONNECTED";
  37.     public final static String ACTION_GATT_SERVICES_DISCOVERED =
  38.             "com.example.bluetooth.le.ACTION_GATT_SERVICES_DISCOVERED";
  39.     public final static String ACTION_DATA_AVAILABLE =
  40.             "com.example.bluetooth.le.ACTION_DATA_AVAILABLE";
  41.     public final static String EXTRA_DATA =
  42.             "com.example.bluetooth.le.EXTRA_DATA";

  43.     public final static UUID UUID_HEART_RATE_MEASUREMENT =
  44.             UUID.fromString(SampleGattAttributes.HEART_RATE_MEASUREMENT);

  45.     // Implements callback methods for GATT events that the app cares about.  For example,
  46.     // connection change and services discovered.
  47.     private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
  48.         @Override
  49.         public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
  50.             String intentAction;
  51.             if (newState == BluetoothProfile.STATE_CONNECTED) {
  52.                 intentAction = ACTION_GATT_CONNECTED;
  53.                 mConnectionState = STATE_CONNECTED;
  54.                 broadcastUpdate(intentAction);
  55.                 Log.i(TAG, "Connected to GATT server.");
  56.                 // Attempts to discover services after successful connection.
  57.                 Log.i(TAG, "Attempting to start service discovery:" +
  58.                         mBluetoothGatt.discoverServices());

  59.             } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
  60.                 intentAction = ACTION_GATT_DISCONNECTED;
  61.                 mConnectionState = STATE_DISCONNECTED;
  62.                 Log.i(TAG, "Disconnected from GATT server.");
  63.                 broadcastUpdate(intentAction);
  64.             }
  65.         }

  66.         @Override
  67.         public void onServicesDiscovered(BluetoothGatt gatt, int status) {
  68.             if (status == BluetoothGatt.GATT_SUCCESS) {
  69.                 broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
  70.             } else {
  71.                 Log.w(TAG, "onServicesDiscovered received: " + status);
  72.             }
  73.         }

  74.         @Override
  75.         public void onCharacteristicRead(BluetoothGatt gatt,
  76.                                          BluetoothGattCharacteristic characteristic,
  77.                                          int status) {
  78.             if (status == BluetoothGatt.GATT_SUCCESS) {
  79.                 broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
  80.             }
  81.         }

  82.         @Override
  83.         public void onCharacteristicChanged(BluetoothGatt gatt,
  84.                                             BluetoothGattCharacteristic characteristic) {
  85.             broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
  86.         }
  87.     };

  88.     private void broadcastUpdate(final String action) {
  89.         final Intent intent = new Intent(action);
  90.         sendBroadcast(intent);
  91.     }

  92.     private void broadcastUpdate(final String action,
  93.                                  final BluetoothGattCharacteristic characteristic) {
  94.         final Intent intent = new Intent(action);

  95.         // This is special handling for the Heart Rate Measurement profile.  Data parsing is
  96.         // carried out as per profile specifications:
  97.         // http://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml
  98.         if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
  99.             int flag = characteristic.getProperties();
  100.             int format = -1;
  101.             if ((flag & 0x01) != 0) {
  102.                 format = BluetoothGattCharacteristic.FORMAT_UINT16;
  103.                 Log.d(TAG, "Heart rate format UINT16.");
  104.             } else {
  105.                 format = BluetoothGattCharacteristic.FORMAT_UINT8;
  106.                 Log.d(TAG, "Heart rate format UINT8.");
  107.             }
  108.             final int heartRate = characteristic.getIntValue(format, 1);
  109.             Log.d(TAG, String.format("Received heart rate: %d", heartRate));
  110.             intent.putExtra(EXTRA_DATA, String.valueOf(heartRate));
  111.         } else {
  112.             // For all other profiles, writes the data formatted in HEX.
  113.             final byte[] data = characteristic.getValue();
  114.             if (data != null && data.length > 0) {
  115.                 final StringBuilder stringBuilder = new StringBuilder(data.length);
  116.                 for(byte byteChar : data)
  117.                     stringBuilder.append(String.format("%02X ", byteChar));
  118.                 intent.putExtra(EXTRA_DATA, new String(data) + "\n" + stringBuilder.toString());
  119.             }
  120.         }
  121.         sendBroadcast(intent);
  122.     }

  123.     public class LocalBinder extends Binder {
  124.         BluetoothLeService getService() {
  125.             return BluetoothLeService.this;
  126.         }
  127.     }

  128.     @Override
  129.     public IBinder onBind(Intent intent) {
  130.         return mBinder;
  131.     }

  132.     @Override
  133.     public boolean onUnbind(Intent intent) {
  134.         // After using a given device, you should make sure that BluetoothGatt.close() is called
  135.         // such that resources are cleaned up properly.  In this particular example, close() is
  136.         // invoked when the UI is disconnected from the Service.
  137.         close();
  138.         return super.onUnbind(intent);
  139.     }

  140.     private final IBinder mBinder = new LocalBinder();

  141.     /**
  142.      * Initializes a reference to the local Bluetooth adapter.
  143.      *
  144.      * @return Return true if the initialization is successful.
  145.      */
  146.     public boolean initialize() {
  147.         // For API level 18 and above, get a reference to BluetoothAdapter through
  148.         // BluetoothManager.
  149.         if (mBluetoothManager == null) {
  150.             mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
  151.             if (mBluetoothManager == null) {
  152.                 Log.e(TAG, "Unable to initialize BluetoothManager.");
  153.                 return false;
  154.             }
  155.         }

  156.         mBluetoothAdapter = mBluetoothManager.getAdapter();
  157.         if (mBluetoothAdapter == null) {
  158.             Log.e(TAG, "Unable to obtain a BluetoothAdapter.");
  159.             return false;
  160.         }

  161.         return true;
  162.     }

  163.     /**
  164.      * Connects to the GATT server hosted on the Bluetooth LE device.
  165.      *
  166.      * @param address The device address of the destination device.
  167.      *
  168.      * @return Return true if the connection is initiated successfully. The connection result
  169.      *         is reported asynchronously through the
  170.      *         {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
  171.      *         callback.
  172.      */
  173.     public boolean connect(final String address) {
  174.         if (mBluetoothAdapter == null || address == null) {
  175.             Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
  176.             return false;
  177.         }

  178.         // Previously connected device.  Try to reconnect.
  179.         if (mBluetoothDeviceAddress != null && address.equals(mBluetoothDeviceAddress)
  180.                 && mBluetoothGatt != null) {
  181.             Log.d(TAG, "Trying to use an existing mBluetoothGatt for connection.");
  182.             if (mBluetoothGatt.connect()) {
  183.                 mConnectionState = STATE_CONNECTING;
  184.                 return true;
  185.             } else {
  186.                 return false;
  187.             }
  188.         }

  189.         final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
  190.         if (device == null) {
  191.             Log.w(TAG, "Device not found.  Unable to connect.");
  192.             return false;
  193.         }
  194.         // We want to directly connect to the device, so we are setting the autoConnect
  195.         // parameter to false.
  196.         mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
  197.         Log.d(TAG, "Trying to create a new connection.");
  198.         mBluetoothDeviceAddress = address;
  199.         mConnectionState = STATE_CONNECTING;
  200.         return true;
  201.     }

  202.     /**
  203.      * Disconnects an existing connection or cancel a pending connection. The disconnection result
  204.      * is reported asynchronously through the
  205.      * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
  206.      * callback.
  207.      */
  208.     public void disconnect() {
  209.         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
  210.             Log.w(TAG, "BluetoothAdapter not initialized");
  211.             return;
  212.         }
  213.         mBluetoothGatt.disconnect();
  214.     }

  215.     /**
  216.      * After using a given BLE device, the app must call this method to ensure resources are
  217.      * released properly.
  218.      */
  219.     public void close() {
  220.         if (mBluetoothGatt == null) {
  221.             return;
  222.         }
  223.         mBluetoothGatt.close();
  224.         mBluetoothGatt = null;
  225.     }

  226.     /**
  227.      * Request a read on a given {@code BluetoothGattCharacteristic}. The read result is reported
  228.      * asynchronously through the {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
  229.      * callback.
  230.      *
  231.      * @param characteristic The characteristic to read from.
  232.      */
  233.     public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
  234.         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
  235.             Log.w(TAG, "BluetoothAdapter not initialized");
  236.             return;
  237.         }
  238.         mBluetoothGatt.readCharacteristic(characteristic);
  239.     }

  240.     /**
  241.      * Enables or disables notification on a give characteristic.
  242.      *
  243.      * @param characteristic Characteristic to act on.
  244.      * @param enabled If true, enable notification.  False otherwise.
  245.      */
  246.     public void setCharacteristicNotification(BluetoothGattCharacteristic characteristic,
  247.                                               boolean enabled) {
  248.         if (mBluetoothAdapter == null || mBluetoothGatt == null) {
  249.             Log.w(TAG, "BluetoothAdapter not initialized");
  250.             return;
  251.         }
  252.         mBluetoothGatt.setCharacteristicNotification(characteristic, enabled);

  253.         // This is specific to Heart Rate Measurement.
  254.         if (UUID_HEART_RATE_MEASUREMENT.equals(characteristic.getUuid())) {
  255.             BluetoothGattDescriptor descriptor = characteristic.getDescriptor(
  256.                     UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
  257.             descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
  258.             mBluetoothGatt.writeDescriptor(descriptor);
  259.         }
  260.     }

  261.     /**
  262.      * Retrieves a list of supported GATT services on the connected device. This should be
  263.      * invoked only after {@code BluetoothGatt#discoverServices()} completes successfully.
  264.      *
  265.      * @return A {@code List} of supported services.
  266.      */
  267.     public List<BluetoothGattService> getSupportedGattServices() {
  268.         if (mBluetoothGatt == null) return null;

  269.         return mBluetoothGatt.getServices();
  270.     }
  271. }
複製代碼


SampleGattAttributes.java
  1. package com.example.android.bluetoothlegatt;

  2. import java.util.HashMap;

  3. /**
  4. * This class includes a small subset of standard GATT attributes for demonstration purposes.
  5. */
  6. public class SampleGattAttributes {
  7.     private static HashMap<String, String> attributes = new HashMap();
  8.     public static String HEART_RATE_MEASUREMENT = "00002a37-0000-1000-8000-00805f9b34fb";
  9.     public static String CLIENT_CHARACTERISTIC_CONFIG = "00002902-0000-1000-8000-00805f9b34fb";

  10.     static {
  11.         // Sample Services.
  12.         attributes.put("0000180d-0000-1000-8000-00805f9b34fb", "Heart Rate Service");
  13.         attributes.put("0000180a-0000-1000-8000-00805f9b34fb", "Device Information Service");
  14.         // Sample Characteristics.
  15.         attributes.put(HEART_RATE_MEASUREMENT, "Heart Rate Measurement");
  16.         attributes.put("00002a29-0000-1000-8000-00805f9b34fb", "Manufacturer Name String");
  17.     }

  18.     public static String lookup(String uuid, String defaultName) {
  19.         String name = attributes.get(uuid);
  20.         return name == null ? defaultName : name;
  21.     }
  22. }
複製代碼




 

臉書網友討論
您需要登錄後才可以回帖 登錄 | 註冊 |

本版積分規則



Archiver|手機版|小黑屋|免責聲明|TShopping

GMT+8, 2020-7-14 00:47 , Processed in 0.062633 second(s), 24 queries .

本論壇言論純屬發表者個人意見,與 TShopping綜合論壇 立場無關 如有意見侵犯了您的權益 請寫信聯絡我們。

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回復 返回頂部 返回列表