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 设备,然后再回收它。多次声明设备会导致奇怪的错误。