目录

管理锻炼记录

应用集成Health Service kit后,通过调用ActivityRecordsController类中的接口来操作用户的锻炼记录。

请先确认应用是否已拥有锻炼记录数据的操作权限:

应用拥有以上权限后,可对用户数据进行如下操作:

  1. 在调用Health Service Kit Android SDK接口时,请确保手机处于亮屏且解锁状态,否则可能会导致接口调用失败,具体错误参见错误码:DISABLE_DATA_OPERATION。
  2. 在调用Health Service Kit SDK接口时,请确保您的应用为前台应用或者与前台应用绑定,否则可能会导致接口调用失败,具体错误参见错误码:APPLICATION_NOT_FORGROUND。
  3. 通过HuaweiHiHealth.getActivityRecordsController()获取ActivityRecordsController时,若传入Activity对象(用于应用包含界面的场景调用),支持触发HMS Core版本强制升级;若传入Context对象(用于应用无界面的场景调用),不支持触发HMS Core版本强制升级。
  4. 在开始调用Health Service Kit接口之前,请阅读Health Service Kit Android SDK接口调用示例章节,了解Health Service Kit Android SDK接口调用流程。

将锻炼记录写入Health Service Kit

用户可以将锻炼记录通过ActivityRecordsController写入到Health Service Kit,具体可执行以下操作:

  1. 创建一个指定活动类型、起止时间、活动统计信息、时区和其他必要信息的ActivityRecord。
  2. 使用ActivityRecord和可选的采样集或聚合采样点数据,创建一个ActivityRecordInsertOptions。
  3. 使用ActivityRecordsController.addActivityRecord方法将ActivityRecordInsertOptions插入Health Service Kit 。

    插入的运动健康数据的锻炼记录数据的开始时间和结束时间均需大于2014年1月1号对应的UNIX时间戳。

将锻炼记录写入Health Service Kit示例代码如下:

// 请注意:
// 1.此处的this为Activity对象
// 2.此处的ActivityRecordsController类的对象,请在每次接口调用前初始化,避免相关Activity或Context失效导致的数据读取失败的问题
final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
Context context = getApplicationContext();

// 构造请求对象的时间区间:开始和结束时间
// 请注意此处设置的开始时间和结束时间均需大于2014年1月1号对应的UNIX时间戳
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.HOUR_OF_DAY, -1);
long startTime = cal.getTimeInMillis();

// 构造运动记录配速对象PaceSummary
// ActivitySummary中的PaceSummary用来承载配速统计数据
ActivitySummary activitySummary = new ActivitySummary();
PaceSummary paceSummary = new PaceSummary();
paceSummary.setAvgPace(247.27626);
paceSummary.setBestPace(212.0);
paceSummary.setPartTimeMap((Map<String, Double>) new HashMap<>().put(1.0, 456));
paceSummary.setPaceMap((Map<String, Double>) new HashMap<>().put(1.0, 263));
activitySummary.setPaceSummary(paceSummary);

// 根据统计数据采集器,构造统计数据样本点,并添加至DataSummary对象
// 创建距离统计数据采样点
SamplePoint samplePointDistance = new SamplePoint.Builder(DataType.DT_CONTINUOUS_DISTANCE_TOTAL).build();
samplePointDistance.setTimeInterval(startTime + 1L, startTime + 300000L, TimeUnit.MILLISECONDS);
samplePointDistance.getFieldValue(Field.FIELD_DISTANCE).setDoubleValue(352.0d);

// 创建速度统计数据采样点
SamplePoint samplePointSpeed = new SamplePoint.Builder(DataType.POLYMERIZE_CONTINUOUS_SPEED_STATISTICS).build();
samplePointSpeed.setTimeInterval(startTime + 1L, startTime + 300000L, TimeUnit.MILLISECONDS);
samplePointSpeed.getFieldValue(Field.FIELD_AVG).setDoubleValue(6.0d);
samplePointSpeed.getFieldValue(Field.FIELD_MIN).setDoubleValue(4.0d);
samplePointSpeed.getFieldValue(Field.FIELD_MAX).setDoubleValue(8.0d);

// ActivitySummary中的DataSummary用来承载统计数据
List<SamplePoint> dataSummary = new ArrayList<>();
dataSummary.add(samplePointDistance);
dataSummary.add(samplePointSpeed);
activitySummary.setDataSummary(dataSummary);

// 构建SampleSection,并添加至SectionSummary
SamplePoint samplePointSectionDistanceSamplePoint = new SamplePoint.Builder(DataType.DT_CONTINUOUS_DISTANCE_TOTAL).build();
samplePointSectionDistanceSamplePoint.getFieldValue(Field.FIELD_DISTANCE).setDoubleValue(400d);
SamplePoint samplePointSectionSpeedSamplePoint = new SamplePoint.Builder(DataType.POLYMERIZE_CONTINUOUS_SPEED_STATISTICS).build();
samplePointSectionSpeedSamplePoint.getFieldValue(Field.FIELD_AVG).setDoubleValue(60.0d);
samplePointSectionSpeedSamplePoint.getFieldValue(Field.FIELD_MIN).setDoubleValue(40.0d);
samplePointSectionSpeedSamplePoint.getFieldValue(Field.FIELD_MAX).setDoubleValue(80.0d);
SampleSection sampleSection = new SampleSection.Builder()
    .setSectionNum(1)
    .setSectionTime(60 * 5L, TimeUnit.SECONDS)
    .setStartTime(startTime + 1L, TimeUnit.MILLISECONDS)
    .setEndTime(startTime + 5 * 60 * 1000L, TimeUnit.MILLISECONDS)
    .setSectionDataList(Arrays.asList(samplePointSectionDistanceSamplePoint, samplePointSectionSpeedSamplePoint))
    .build();
activitySummary.setSectionSummary(Collections.singletonList(sampleSection));

// 构造运动记录,设置统计数据ActivitySummary对象
ActivityRecord activityRecord = new ActivityRecord.Builder()
    .setName("AddActivityRecord")
    .setDesc("This is ActivityRecord add test!")
    .setId("MyAddActivityRecordId")
    .setActivityTypeId(HiHealthActivities.RUNNING)
    .setStartTime(startTime, TimeUnit.MILLISECONDS)
    .setEndTime(endTime, TimeUnit.MILLISECONDS)
    .setActivitySummary(activitySummary)
    .setTimeZone("+0800")
    .setMetadata("this is my test") 
    .build();

// 创建一个明细数据采集器
DataCollector dataCollector = new com.huawei.hms.hihealth.data.DataCollector.Builder()
    .setDataType(DataType.DT_INSTANTANEOUS_STEPS_RATE)
    .setDataGenerateType(DataCollector.DATA_TYPE_RAW)
    .setPackageName(getApplicationContext())
    .setDataCollectorName("AddActivityRecord")
    .build();
// 根据数据采集器,构造采样集
SampleSet sampleSet = SampleSet.create(dataCollector);

// 构造步频增量明细(DT_INSTANTANEOUS_STEPS_RATE)采样点对象,添加到明细采样集
SamplePoint samplePointStepDelta = sampleSet.createSamplePoint().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS);
samplePointStepDelta.getFieldValue(Field.FIELD_STEP_RATE).setDoubleValue(100.0d);
sampleSet.addSample(samplePointStepDelta);

// 构造运动记录请求,设置运动记录activityRecord对象与其关联的明细采样集
ActivityRecordInsertOptions insertOption =
    new ActivityRecordInsertOptions.Builder().setActivityRecord(activityRecord).addSampleSet(sampleSet).build();

// 调用addActivityRecord接口添加运动记录
Task<Void> addActivityRecordTask = activityRecordsController.addActivityRecord(insertOption);
addActivityRecordTask.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Log.i("ActivityRecords","ActivityRecord was added successfully!");
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        Log.i("ActivityRecord","Failed to add the ActivityRecord:" + e.getMessage());
    }
});

删除Health Service Kit的锻炼记录

用户可以通过ActivityRecordsController删除锻炼记录,具体可执行以下操作:

  1. 根据activityRecordIds或起止时间创建一个ActivityRecordDeleteOptions。
  2. 使用ActivityRecordsController.deleteActivityRecord方法将满足ActivityRecordDeleteOptions的锻炼记录数据从Health Service Kit中删除 。
    1. 删除的运动健康数据的锻炼记录数据的开始时间和结束时间均需大于2014年1月1号对应的UNIX时间戳。
    2. 删除锻炼记录数据时,若activityRecordIds不为空则根据activityRecordIds删除,否则根据起止时间删除。

删除Health Service Kit的锻炼记录示例代码如下:

// 请注意:
// 1.此处的this为Activity对象
// 2.此处的ActivityRecordsController类的对象,请在每次接口调用前初始化,避免相关Activity或Context失效导致的数据读取失败的问题
final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);

// 构造请求对象的时间区间:开始和结束时间
// 请注意此处设置的开始时间和结束时间均需大于2014年1月1号对应的UNIX时间戳
Calendar cal = Calendar.getInstance();
Date now = new Date();
cal.setTime(now);
long endTime = cal.getTimeInMillis();
cal.add(Calendar.DAY_OF_YEAR, -2);
long startTime = cal.getTimeInMillis();
// 构造请求对象的subDataTypeList
List<DataType> subDataTypeList = Collections.singletonList(DataType.DT_CONTINUOUS_STEPS_DELTA);
// 构造请求对象的activityRecordIds
List<String> activityRecordIds = Collections.singletonList("MyAddActivityRecordId");

// 构造删除对象请求体
ActivityRecordDeleteOptions deleteRequest = new ActivityRecordDeleteOptions.Builder()
    .setSubDataTypeList(subDataTypeList)
    .setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
    .setActivityRecordIds(activityRecordIds)
    .build();

// 调用deleteActivityRecord接口删除运动记录
Task<Void> deleteTask = activityRecordsController.deleteActivityRecord(deleteRequest);
deleteTask.addOnSuccessListener(new OnSuccessListener<Void>() {
    @Override
    public void onSuccess(Void aVoid) {
        Log.i("ActivityRecords","ActivityRecord was deleted successfully!");
    }
}).addOnFailureListener(new OnFailureListener() {
    @Override
    public void onFailure(Exception e) {
        Log.i("ActivityRecord","Failed to delete the ActivityRecord:" + e.getMessage());
    }
});

读取Health Service Kit的锻炼记录

用户可以从Health Service Kit中获取到满足条件的锻炼记录和关联数据信息。例如,可以读取特定时间段内包含的特定运动类型数据的所有锻炼记录,也可以读取感兴趣的其他应用创建的锻炼记录。ActivityRecord中的activitySummary可以让用户获取到对应锻炼记录的统计类型数据,明细数据展示在ActivityRecordReply的sampleSet中。

  1. 要获取符合某些条件的锻炼记录列表,先要创建一个ActivityRecordReadOptions实例。
    1. 调用查询运动健康数据的锻炼记录接口,接口参数中的开始时间和结束时间均需大于2014年1月1号对应的UNIX时间戳。
    2. 调用查询运动健康数据的锻炼记录接口,接口参数中的开始时间和结束时间之间的时间跨度不允许超过31天。
    3. ActivityRecordReadOptions实例中的read参数携带具体的数据类型可从专业运动中获取,如果不携带,可能读取不到华为运动健康App上的运动记录数据。
  2. 使用ActivityRecordsController.getActivityRecord方法获取数据。
    // 请注意:
    // 1.此处的this为Activity对象
    // 2.此处的ActivityRecordsController类的对象,请在每次接口调用前初始化,避免相关Activity或Context失效导致的数据读取失败的问题
    final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
    
    // 1.构造请求对象的时间区间:开始和结束时间
    // 请注意此处设置的开始时间和结束时间均需大于2014年1月1号对应的UNIX时间戳
    Calendar cal = Calendar.getInstance();
    Date now = new Date();
    cal.setTime(now);
    long endTime = cal.getTimeInMillis();
    cal.add(Calendar.DAY_OF_YEAR, -1);
    long startTime = cal.getTimeInMillis();
    
    // 2.构造读取运动记录请求体,设置请求时间范围
    // 以下示例为读取给定时间段内的运动记录,同时附带读取运动期间的步频数据
    // 如果需要读取其他类型的运动期间的详情数据,请开发者自行添加对应的数据类型
    ActivityRecordReadOptions readOption =
        new ActivityRecordReadOptions.Builder().setTimeInterval(startTime, endTime, TimeUnit.MILLISECONDS)
            .readActivityRecordsFromAllApps()
            .read(DataType.DT_INSTANTANEOUS_STEPS_RATE)
            .build();
    
    // 3.调用getActivityRecord接口,根据请求体条件获取Health Service Kit中的运动记录
    Task<ActivityRecordReply> getTask = activityRecordsController.getActivityRecord(readOption);
    getTask.addOnSuccessListener(new OnSuccessListener<ActivityRecordReply>() {
        @Override
        public void onSuccess(ActivityRecordReply activityRecordReply) {
            Log.i("ActivityRecords","Obtaining activity records succeeded.!");
            // 打印查询到的运动记录列表
            List<ActivityRecord> activityRecordList = activityRecordReply.getActivityRecords();
            for (ActivityRecord activityRecord : activityRecordList) {
                if (activityRecord == null) {
                    continue;
                }
                DateFormat dateFormat = DateFormat.getDateInstance();
                DateFormat timeFormat = DateFormat.getTimeInstance();
                Log.i("ActivityRecordSample",
                    "Returned for ActivityRecord: " + activityRecord.getName() + "\n\tActivityRecord Identifier is "
                    + activityRecord.getId() + "\n\tActivityRecord created by app is "
                    + activityRecord.getPackageName() + "\n\tDescription: " + activityRecord.getDesc()
                    + "\n\tStart: " + dateFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS))
                    + " " + timeFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + "\n\tEnd: "
                    + dateFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + "\n\tActivity:"
                    + activityRecord.getActivityType() + "\n\tTimeZone:" + activityRecord.getTimeZone() 
                    + "\n\tmetadata:" + activityRecord.getMetadata());
                // 打印每个运动记录里的ActivitySummary,printActivitySummary为自定义的输出函数
                if (activityRecord.getActivitySummary() != null) {
                    printActivitySummary(activityRecord.getActivitySummary());
                }
                // 打印每个运动记录对应的明细数据点
                for (SampleSet sampleSet : activityRecordReply.getSampleSet(activityRecord)) {
                    Log.i("ActivityRecordSample",
                        "Returned for SamplePoint and Data type: " + sampleSet.getDataType().getName());
                    for (SamplePoint dp : sampleSet.getSamplePoints()) {
                        Log.i("ActivityRecordSample", "SamplePoint:");
                        Log.i("ActivityRecordSample", "DataCollector:" + dp.getDataCollector().toString());
                        Log.i("ActivityRecordSample", "\tType: " + dp.getDataType().getName());
                        Log.i("ActivityRecordSample",
                            "\tStart: " + dateFormat.format(dp.getStartTime(TimeUnit.MILLISECONDS)));
                        Log.i("ActivityRecordSample",
                            "\tEnd: " + dateFormat.format(dp.getEndTime(TimeUnit.MILLISECONDS)));
                        for (Field field : dp.getDataType().getFields()) {
                            Log.i("ActivityRecordSample",
                                "\tField: " + field.toString() + " Value: " + dp.getFieldValue(field));
                        }
                    }
                }
            }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.i("ActivityRecord","Failed to read the ActivityRecord:" + e.getMessage());
        }
    });

上面的代码片段中用到辅助函数:

  1. Health Service Kit返回ActivitySummary数据的显示函数printActivitySummary(ActivitySummary activitySummary)。函数的代码示例如下:
    // 自定义ActivitySummary输出函数
    public void printActivitySummary(ActivitySummary activitySummary) {
        List<SamplePoint> dataSummary = activitySummary.getDataSummary();
        Log.i("ActivityRecordSample", "\n打印统计数据: ");
        Log.i("ActivityRecordSample", "\nActivitySummary\n\t DataSummary: ");
        // 打印统计数据点
        for (SamplePoint samplePoint : dataSummary) {
            Log.i("ActivityRecordSample", "\n\t samplePoint: \n\t DataCollector" + samplePoint.getDataCollector() + "\n\t DataType" 
                + samplePoint.getDataType() + "\n\t StartTime" + samplePoint.getStartTime(TimeUnit.MILLISECONDS) + "\n\t EndTime"
                + samplePoint.getEndTime(TimeUnit.MILLISECONDS) + "\n\t SamplingTime" + samplePoint.getSamplingTime(TimeUnit.MILLISECONDS)
                + "\n\t FieldValues" + samplePoint.getFieldValues());
        }
        // 打印配速信息
        PaceSummary paceSummary = activitySummary.getPaceSummary();
        Log.i("ActivityRecordSample", "\n\t PaceSummary: \n\t AvgPace" + paceSummary.getAvgPace() + "\n\t BestPace" 
            + paceSummary.getBestPace() + "\n\t PaceMap" + paceSummary.getPaceMap() + "\n\t PartTimeMap"
            + paceSummary.getPartTimeMap() + "\n\t SportHealthPaceMap" + paceSummary.getSportHealthPaceMap());
        printSectionSummary(activitySummary.getSectionSummary());
    }
  2. Health Service Kit返回数据的显示函数printSectionSummary(List<SampleSection> sectionSummary)。函数的代码示例如下:
    // 自定义返回数据的显示函数
    private void printSectionSummary(List<SampleSection> sectionSummary) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    
        for (SampleSection sampleSection : sectionSummary) {
            Log.i("ActivityRecordSample", "SampleSection.SectionNum: " + sampleSection.getSectionNum());
            Log.i("ActivityRecordSample", "SampleSection.sectionTime: " + sampleSection.getSectionTime(TimeUnit.SECONDS));
            Log.i("ActivityRecordSample", "SampleSection.startTime: " + dateFormat.format(new Date(sampleSection.getStartTime(TimeUnit.MILLISECONDS))));
            Log.i("ActivityRecordSample", "SampleSection.endTime: " + dateFormat.format(new Date(sampleSection.getEndTime(TimeUnit.MILLISECONDS))));
            Log.i("ActivityRecordSample", "SampleSection.sectionDataList:");
            for (SamplePoint samplePoint : sampleSection.getSectionDataList()) {
                for (Field field : samplePoint.getDataType().getFields()) {
                    Log.i("ActivityRecordSample", "Field: " + field.getName() + " Value: " + samplePoint.getFieldValue(field));
                }
            }
        }
    }

根据实时数据创建锻炼记录

对正在进行的活动创建ActivityRecord,活动过程中的运动数据在Health Service Kit中与ActivityRecord隐式关联。

  1. 启动ActivityRecord。

    应用使用ActivityRecordsController.beginActivityRecord方法启动一个锻炼记录,示例如下:

    // 请注意:
    // 1.此处的this为Activity对象
    // 2.此处的ActivityRecordsController类的对象,请在每次接口调用前初始化,避免相关Activity或Context失效导致的数据读取失败的问题
    final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
    
    // 构造运动记录开始时间
    long startTime = Calendar.getInstance().getTimeInMillis();
    // 构造ActivityRecord对象,设置运动记录开始时间 
    ActivityRecord activityRecord = new ActivityRecord.Builder()
        .setId("MyBeginActivityRecordId")
        .setName("BeginActivityRecord")
        .setDesc("This is ActivityRecord begin test!")
        .setActivityTypeId(HiHealthActivities.RUNNING)
        .setStartTime(startTime, TimeUnit.MILLISECONDS)
        .build();
    
    // 调用启动新运动记录API接口beginActivityRecord
    Task<Void> task1 = activityRecordsController.beginActivityRecord(activityRecord);
    // 添加启动ActivityRecord成功
    task1.addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            Log.i("ActivityRecords","MyActivityRecord started successfully.");
        }
    // 添加启动ActivityRecord失败
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.i("ActivityRecords","Failed to begin MyActivityRecord:" +  e.getMessage());
        }
    });

  2. 停止ActivityRecord。

    应用中使用ActivityRecordsController.endActivityRecord方法停止指定的锻炼记录,示例如下:

    // 请注意此处的this为Activity对象
    final ActivityRecordsController activityRecordsController = HuaweiHiHealth.getActivityRecordsController(this);
    
    // 调用endActivityRecord接口停止运动记录,入参为ActivityRecord的ID字符串或者null
    // 入参为ID字符串时,停止当前应用指定ID的运动记录
    // 入参为null时,停止该应用当前所有的未停止运动记录
    Task<List<ActivityRecord>> endTask = activityRecordsController.endActivityRecord("MyBeginActivityRecordId");
    endTask.addOnSuccessListener(new OnSuccessListener<List<ActivityRecord>>() {
        @Override
        public void onSuccess(List<ActivityRecord> activityRecords) {
            Log.i("ActivityRecords","MyActivityRecord End success");
            // 返回停止成功的运动记录列表
            if (activityRecords.size() > 0) {
                for (ActivityRecord activityRecord : activityRecords) {
                    DateFormat dateFormat = DateFormat.getDateInstance();
                    DateFormat timeFormat = DateFormat.getTimeInstance();
                    Log.i("ActivityRecords", "Returned for ActivityRecord: " + activityRecord.getName() + "\n\tActivityRecord Identifier is "
                        + activityRecord.getId() + "\n\tActivityRecord created by app is " + activityRecord.getPackageName()
                        + "\n\tDescription: " + activityRecord.getDesc() + "\n\tStart: "
                        + dateFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + " "
                        + timeFormat.format(activityRecord.getStartTime(TimeUnit.MILLISECONDS)) + "\n\tEnd: "
                        + dateFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + " "
                        + timeFormat.format(activityRecord.getEndTime(TimeUnit.MILLISECONDS)) + "\n\tActivity:"
                        + activityRecord.getActivityType());
                }
            } else {
                // 没有停止成功返回null
                Log.i("ActivityRecords","MyActivityRecord End response is null");
            }
        }
    }).addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(Exception e) {
            Log.i("ActivityRecords","MyActivityRecord End fail:" + e.getMessage());     
        }
    });