Android BLEの概要

83472 ワード

         ,          ,               ,     ,       !   
        ,    BLE   ,           ,      。   
 BLE     Bluetooth Low Energy           ,   API18(Android 4.3)      ,         BLE       

もしあなたのappがBLEを使うだけなら、manifestにあります.xmlに以下の権限を加えればいいです.
 android:name="android.hardware.bluetooth_le" android:required="true"/>

    App     BLE( API<18)        ,           required  false。  ,       ,
// 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();
}

コードの役割は、デバイスがBLEをサポートしていない場合、Toastをポップアップしてユーザーに伝えることです.
権限を追加すると、まずBLEを開くためにコードを叩くことができます.ここではBluetoothAdapterとBluetoothMangerオブジェクトに関連しています.BLEをサポートするandroidデバイスがあり、BluetoothAdapterが1つしかありません.BluetoothMangerで取得します.
// Initializes Bluetooth adapter.
BluetoothAdapter mBluetoothAdapter;
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
そしてBLEを開く:
private BluetoothAdapter mBluetoothAdapter;
...
// Ensures Bluetooth is available on the device and it is enabled. If not,
// displays a dialog requesting user permission to enable Bluetooth.
if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);//        ,        BLE
}
周辺のBluetoothデバイスを検索します.
/**
 * Activity for scanning and displaying available BLE devices.
 */
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);//mLeScanCallback            ,       
                }
            }, SCAN_PERIOD);

            mScanning = true;
            mBluetoothAdapter.startLeScan(mLeScanCallback);//    
        } else {
            mScanning = false;
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
        ...//
    }
...//   ...   ListActivity Adapter   , mLeScanCallback       
}
LeScanCallbackは、デバイスが周辺の他のBluetoothデバイスを検索するときにコールバックするインタフェースオブジェクトです.
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();
           }
       });
   }
};
//////////////////////////////////////////////////////////////////////////////////
public class LeDeviceListAdapter extends BaseAdapter{
     
     private List devices;
     
     public LeDeviceListAdapter(){
          devices = new ArrayList<>();
          .....
     }

     public void addDevice(BluetoothDevice mdevice){
          if(!devices.contains(mdevice)){
           
           devices.add(mdevice);
     }

     }
.......
      @Override
      public View getView(int position ,View view ,ViewGroup viewGroup){
           BluetoothDevice bd = devices.get(position);
           String deviceName = bd.getName();//         ,      ListActivity    
    
}

 
.....................//  viewholder       

}

           
      GATT    ,GATT              ,  Characteristic、DescriptorService     
BLE     Service、Characteristic、Descriptor,      UUID       。    4.0     
    Service,  Service      Characteristic,  Characteristic    Value   Descriptor
,  Descriptor    Value

  GATT Server
mBluetoothGatt = device.connectGatt(Context context, boolean autoConnect, BluetoothGattCallback mGattCallback);

BluetoothGattCallback            ,     ,              

BluetoothGattCallbackコールバックの実装:
// A service that interacts with the BLE device via the 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);
            }
        }
     ...
    };
...
}
長いコードですが、Bluetoothの接続状態を傍受し、システムにブロードキャストを送信します.
broadcastUpdate()   :
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); }
で放送が出ていますので、当然放送を処理します
// 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));
        }
    }
};
     ,        !!!      ,     .......
 
  
public boolean sendCharacteristic(){

String kUUIDWriteCommand = "0000FEC7-0000-1000-8000-00805F9B34FB";//"0000FF01-0000-1000-8000-00805F9B34FB";
     
    
     UUID writeUUID = UUID.fromString(kUUIDWriteCommand);
     
     
     BluetoothGattCharacteristic btWriteGattChar = gattService.getCharacteristic(writeUUID);
     
 		
		   			
      boolean right = divideFrameBleSendData(data, btWriteGattChar);

      
}

Bluetoothに送信されるデータはパケットで送信され、長すぎるデータは一度に公開されます.

private static boolean  divideFrameBleSendData(byte[] data, BluetoothGattCharacteristic btWriteGattChar){ boolean right = false;  int tmpLen = data.length;  int start = 0;  int end = 0;  while (tmpLen > 0){  byte[] sendData = new byte[21];  if (tmpLen >= 20){  end += 20;  sendData = Arrays.copyOfRange(data, start, end);  start += 20;  tmpLen -= 20;  }  else{  end += tmpLen;  sendData = Arrays.copyOfRange(data, start, end);  tmpLen = 0;  }    right = btWriteGattChar.setValue(sendData);//送信データif(!right){MyUtil.writeLog("btWriteGattChar.setValue false!data="+MyUtil.binToHex(data,0,data.length));return false;  }     right = mBluetoothGatt.writeCharacteristic(btWriteGattChar);   if (!right) return right;  }    return right; }
  ,         (    data         )

       ....