Google Fit API 未从蓝牙腕带 (SWR12) 接收数据
Google Fit API not receiving data from Bluetooth wrist band (SWR12)
我正在编写一个 Android 应用程序,它使用 Google Fit API 连接到蓝牙腕带,目的是从传感器收集心率信息。以下是我正在使用的功能:
在主应用中 activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
if (!checkPermissions()) {
requestPermissions();
}
if (!checkPermissionsBody()) {
requestPermissionsBody();
权限请求函数:
// Now we need a function to check permissions
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
// Now we need a function to check permissions body sensors
private boolean checkPermissionsBody() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.BODY_SENSORS);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
// If permissions are not given, we need to request permissions
private void requestPermissions() {
Log.d(TAG,"getting permissions");
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
Log.d(TAG,String.valueOf(Manifest.permission.ACCESS_FINE_LOCATION));
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
// If permissions are not given, we need to request permissions
private void requestPermissionsBody() {
Log.d(TAG,"getting permissions");
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.BODY_SENSORS);
Log.d(TAG,String.valueOf(Manifest.permission.BODY_SENSORS));
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
onResume 函数:
protected void onResume() {
super.onResume();
// This ensures that if the user denies the permissions then uses Settings to re-enable
// them, the app will start working.
buildFitnessClient();
// now find the bluetooth devices
buildBLE();
// Connect to the Client
mClient.connect();
// Search for the data sources
findFitnessDataSources();
构建 API 客户端:
private void buildFitnessClient() {
if (mClient == null && checkPermissions()&& checkPermissionsBody()) {
mClient = new GoogleApiClient.Builder(this)
.addScope(new Scope(Scopes.FITNESS_BODY_READ))
.addApi(Fitness.SENSORS_API)
.addApi(Fitness.BLE_API)
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected!!!");
// Now you can make calls to the Fitness APIs.
}
@Override
public void onConnectionSuspended(int i) {
// If your connection to the sensor gets lost at some point,
// you'll be able to determine the reason and react to it here.
if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
Log.i(TAG, "Connection lost. Cause: Network Lost.");
} else if (i
== GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
Log.i(TAG,
"Connection lost. Reason: Service Disconnected");
}
}
}
)
.enableAutoManage(this, 0, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "Google Play services connection failed. Cause: " +
result.toString());
Snackbar.make(
MainActivity.this.findViewById(R.id.activity_main),
"Exception while connecting to Google Play services: " +
result.getErrorMessage(),
Snackbar.LENGTH_INDEFINITE).show();
}
})
.build();
}
}
}
现在找到健身源:
private void findFitnessDataSources() {
// [START find_data_sources]
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
// At least one datatype must be specified.
.setDataTypes(DataType.TYPE_HEART_RATE_BPM)
// Can specify whether data type is raw or derived.
//.setDataSourceTypes(DataSource.TYPE_RAW)
.build())
.setResultCallback(new ResultCallback<DataSourcesResult>() {
@Override
public void onResult(DataSourcesResult dataSourcesResult) {
Log.i(TAG, "Result: " + dataSourcesResult.getStatus().toString());
for (DataSource dataSource : dataSourcesResult.getDataSources()) {
Log.i(TAG, "Data source found: " + dataSource.toString());
Log.i(TAG, "Data Source type: " + dataSource.getDataType().getName());
//Let's register a listener to receive Activity data!
if (dataSource.getDataType().equals(DataType.TYPE_HEART_RATE_BPM)
&& mListener == null) {
Log.i(TAG, "Data source for Heart Rate found! Registering.");
registerFitnessDataListener(dataSource,
DataType.TYPE_HEART_RATE_BPM);
}
}
}
});
构建 BLE 功能 - 这是您找到蓝牙设备的地方:
private void buildBLE(){
BleScanCallback callback = new BleScanCallback() {
@Override
public void onDeviceFound(BleDevice device) {
Log.d(TAG,"Found bluetooth Device");
// A device that provides the requested data types is available
PendingResult<Status> pendingResult =
Fitness.BleApi.claimBleDevice(mClient, device);
Log.d(TAG,"Claimed bluetooth Device");
}
@Override
public void onScanStopped() {
// The scan timed out or was interrupted
Log.d(TAG,"Scan was interruped");
}
};
StartBleScanRequest request = new StartBleScanRequest.Builder()
.setDataTypes(DataType.TYPE_HEART_RATE_BPM)
.setBleScanCallback(callback)
.build();
if (mClient != null){
PendingResult<Status> pendingResult =
Fitness.BleApi.startBleScan(mClient, request);
Log.d(TAG,"Find Sources");
Log.d(TAG,"Pending result: "+pendingResult.toString());
} else {
Log.d(TAG,"API client is null");
}
}
最后注册监听器:
private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {
// [START register_data_listener]
Log.i(TAG,"Listener Started");
mListener = new OnDataPointListener() {
@Override
public void onDataPoint(DataPoint dataPoint) {
for (Field field : dataPoint.getDataType().getFields()) {
Value val = dataPoint.getValue(field);
Log.i(TAG, "Detected DataPoint field: " + field.getName());
Log.i(TAG, "Detected DataPoint value: " + val);
}
}
};
Fitness.SensorsApi.add(
mClient,
new SensorRequest.Builder()
.setDataSource(dataSource) // Optional but recommended for custom data sets.
.setDataType(dataType) // Can't be omitted.
.setSamplingRate(10, TimeUnit.SECONDS)
.build(),
mListener)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Listener registered!");
} else {
Log.i(TAG, "Listener not registered.");
}
}
});
// [END register_data_listener]
}
这些功能中的大部分都直接取自此处的 google 安装指南:https://developers.google.com/fit/
问题在于,当我使用这些功能时,蓝牙设备会在重复循环中被发现和声明。所以调试输出看起来像这样:
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Scan cancelled");
尽管设备被多次认领,但仍有数据传来。即使侦听器已注册,也是如此。我知道是因为我看到了调试行:
Log.i(TAG,"Listener Started");
所以我的问题是,这段代码有什么不合适的地方吗?有人用过吗?
对于任何有兴趣使用 Google Fit with BLE 设备的人,这个问题的答案是您一次只能注册一个设备。当您重置蓝牙适配器时,您必须在应用程序中重新申请 BLE 设备。因此,正确的程序是取消声明 BLE 设备,然后再回收它。多次声明设备会导致奇怪的错误。
我正在编写一个 Android 应用程序,它使用 Google Fit API 连接到蓝牙腕带,目的是从传感器收集心率信息。以下是我正在使用的功能:
在主应用中 activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
if (!checkPermissions()) {
requestPermissions();
}
if (!checkPermissionsBody()) {
requestPermissionsBody();
权限请求函数:
// Now we need a function to check permissions
private boolean checkPermissions() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
// Now we need a function to check permissions body sensors
private boolean checkPermissionsBody() {
int permissionState = ActivityCompat.checkSelfPermission(this,
Manifest.permission.BODY_SENSORS);
return permissionState == PackageManager.PERMISSION_GRANTED;
}
// If permissions are not given, we need to request permissions
private void requestPermissions() {
Log.d(TAG,"getting permissions");
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.ACCESS_FINE_LOCATION);
Log.d(TAG,String.valueOf(Manifest.permission.ACCESS_FINE_LOCATION));
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
// If permissions are not given, we need to request permissions
private void requestPermissionsBody() {
Log.d(TAG,"getting permissions");
boolean shouldProvideRationale =
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.BODY_SENSORS);
Log.d(TAG,String.valueOf(Manifest.permission.BODY_SENSORS));
// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
Log.i(TAG, "Displaying permission rationale to provide additional context.");
Snackbar.make(
findViewById(R.id.activity_main),
R.string.permission_rationale,
Snackbar.LENGTH_INDEFINITE)
.setAction(R.string.ok, new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
})
.show();
} else {
Log.i(TAG, "Requesting permission");
// Request permission. It's possible this can be auto answered if device policy
// sets the permission in a given state or the user denied the permission
// previously and checked "Never ask again".
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.BODY_SENSORS},
REQUEST_PERMISSIONS_REQUEST_CODE);
}
}
onResume 函数:
protected void onResume() {
super.onResume();
// This ensures that if the user denies the permissions then uses Settings to re-enable
// them, the app will start working.
buildFitnessClient();
// now find the bluetooth devices
buildBLE();
// Connect to the Client
mClient.connect();
// Search for the data sources
findFitnessDataSources();
构建 API 客户端:
private void buildFitnessClient() {
if (mClient == null && checkPermissions()&& checkPermissionsBody()) {
mClient = new GoogleApiClient.Builder(this)
.addScope(new Scope(Scopes.FITNESS_BODY_READ))
.addApi(Fitness.SENSORS_API)
.addApi(Fitness.BLE_API)
.addConnectionCallbacks(
new GoogleApiClient.ConnectionCallbacks() {
@Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected!!!");
// Now you can make calls to the Fitness APIs.
}
@Override
public void onConnectionSuspended(int i) {
// If your connection to the sensor gets lost at some point,
// you'll be able to determine the reason and react to it here.
if (i == GoogleApiClient.ConnectionCallbacks.CAUSE_NETWORK_LOST) {
Log.i(TAG, "Connection lost. Cause: Network Lost.");
} else if (i
== GoogleApiClient.ConnectionCallbacks.CAUSE_SERVICE_DISCONNECTED) {
Log.i(TAG,
"Connection lost. Reason: Service Disconnected");
}
}
}
)
.enableAutoManage(this, 0, new GoogleApiClient.OnConnectionFailedListener() {
@Override
public void onConnectionFailed(ConnectionResult result) {
Log.i(TAG, "Google Play services connection failed. Cause: " +
result.toString());
Snackbar.make(
MainActivity.this.findViewById(R.id.activity_main),
"Exception while connecting to Google Play services: " +
result.getErrorMessage(),
Snackbar.LENGTH_INDEFINITE).show();
}
})
.build();
}
}
}
现在找到健身源:
private void findFitnessDataSources() {
// [START find_data_sources]
// Note: Fitness.SensorsApi.findDataSources() requires the ACCESS_FINE_LOCATION permission.
Fitness.SensorsApi.findDataSources(mClient, new DataSourcesRequest.Builder()
// At least one datatype must be specified.
.setDataTypes(DataType.TYPE_HEART_RATE_BPM)
// Can specify whether data type is raw or derived.
//.setDataSourceTypes(DataSource.TYPE_RAW)
.build())
.setResultCallback(new ResultCallback<DataSourcesResult>() {
@Override
public void onResult(DataSourcesResult dataSourcesResult) {
Log.i(TAG, "Result: " + dataSourcesResult.getStatus().toString());
for (DataSource dataSource : dataSourcesResult.getDataSources()) {
Log.i(TAG, "Data source found: " + dataSource.toString());
Log.i(TAG, "Data Source type: " + dataSource.getDataType().getName());
//Let's register a listener to receive Activity data!
if (dataSource.getDataType().equals(DataType.TYPE_HEART_RATE_BPM)
&& mListener == null) {
Log.i(TAG, "Data source for Heart Rate found! Registering.");
registerFitnessDataListener(dataSource,
DataType.TYPE_HEART_RATE_BPM);
}
}
}
});
构建 BLE 功能 - 这是您找到蓝牙设备的地方:
private void buildBLE(){
BleScanCallback callback = new BleScanCallback() {
@Override
public void onDeviceFound(BleDevice device) {
Log.d(TAG,"Found bluetooth Device");
// A device that provides the requested data types is available
PendingResult<Status> pendingResult =
Fitness.BleApi.claimBleDevice(mClient, device);
Log.d(TAG,"Claimed bluetooth Device");
}
@Override
public void onScanStopped() {
// The scan timed out or was interrupted
Log.d(TAG,"Scan was interruped");
}
};
StartBleScanRequest request = new StartBleScanRequest.Builder()
.setDataTypes(DataType.TYPE_HEART_RATE_BPM)
.setBleScanCallback(callback)
.build();
if (mClient != null){
PendingResult<Status> pendingResult =
Fitness.BleApi.startBleScan(mClient, request);
Log.d(TAG,"Find Sources");
Log.d(TAG,"Pending result: "+pendingResult.toString());
} else {
Log.d(TAG,"API client is null");
}
}
最后注册监听器:
private void registerFitnessDataListener(DataSource dataSource, DataType dataType) {
// [START register_data_listener]
Log.i(TAG,"Listener Started");
mListener = new OnDataPointListener() {
@Override
public void onDataPoint(DataPoint dataPoint) {
for (Field field : dataPoint.getDataType().getFields()) {
Value val = dataPoint.getValue(field);
Log.i(TAG, "Detected DataPoint field: " + field.getName());
Log.i(TAG, "Detected DataPoint value: " + val);
}
}
};
Fitness.SensorsApi.add(
mClient,
new SensorRequest.Builder()
.setDataSource(dataSource) // Optional but recommended for custom data sets.
.setDataType(dataType) // Can't be omitted.
.setSamplingRate(10, TimeUnit.SECONDS)
.build(),
mListener)
.setResultCallback(new ResultCallback<Status>() {
@Override
public void onResult(Status status) {
if (status.isSuccess()) {
Log.i(TAG, "Listener registered!");
} else {
Log.i(TAG, "Listener not registered.");
}
}
});
// [END register_data_listener]
}
这些功能中的大部分都直接取自此处的 google 安装指南:https://developers.google.com/fit/
问题在于,当我使用这些功能时,蓝牙设备会在重复循环中被发现和声明。所以调试输出看起来像这样:
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Found bluetooth Device");
Log.d(TAG,"Claimed bluetooth Device");
Log.d(TAG,"Scan cancelled");
尽管设备被多次认领,但仍有数据传来。即使侦听器已注册,也是如此。我知道是因为我看到了调试行: Log.i(TAG,"Listener Started");
所以我的问题是,这段代码有什么不合适的地方吗?有人用过吗?
对于任何有兴趣使用 Google Fit with BLE 设备的人,这个问题的答案是您一次只能注册一个设备。当您重置蓝牙适配器时,您必须在应用程序中重新申请 BLE 设备。因此,正确的程序是取消声明 BLE 设备,然后再回收它。多次声明设备会导致奇怪的错误。