با داده های تاریخی کار کنید

History API به برنامه شما امکان می‌دهد عملیات‌های انبوه را در فروشگاه تناسب اندام انجام دهد: خواندن، درج، به‌روزرسانی، و حذف داده‌های مربوط به سلامت و تندرستی. برای انجام کارهای زیر از History API استفاده کنید:

  • داده‌های سلامت و تندرستی را که با استفاده از برنامه‌های دیگر درج یا ضبط شده‌اند بخوانید.
  • داده های دسته ای را به Google Fit وارد کنید.
  • داده‌ها را در Google Fit به‌روزرسانی کنید.
  • داده های تاریخی را که برنامه شما قبلاً ذخیره کرده بود حذف کنید.

برای درج داده‌هایی که حاوی فراداده‌های جلسه هستند، از Sessions API استفاده کنید.

داده ها را بخوانید

بخش های زیر نحوه خواندن انواع مختلف داده های انبوه را پوشش می دهد.

اطلاعات دقیق و انبوه را بخوانید

برای خواندن داده های تاریخی، یک نمونه DataReadRequest ایجاد کنید.

کاتلین

// Read the data that's been collected throughout the past week.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusWeeks(1)
Log.i(TAG, "Range Start: $startTime")
Log.i(TAG, "Range End: $endTime")

val readRequest =
    DataReadRequest.Builder()
        // The data request can specify multiple data types to return,
        // effectively combining multiple data queries into one call.
        // This example demonstrates aggregating only one data type.
        .aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
        // Analogous to a "Group By" in SQL, defines how data should be
        // aggregated.
        // bucketByTime allows for a time span, whereas bucketBySession allows
        // bucketing by <a href="/fit/android/using-sessions">sessions</a>.
        .bucketByTime(1, TimeUnit.DAYS)
        .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build()

جاوا

// Read the data that's been collected throughout the past week.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusWeeks(1);
Log.i(TAG, "Range Start: $startTime");
Log.i(TAG, "Range End: $endTime");

DataReadRequest readRequest = new DataReadRequest.Builder()
        // The data request can specify multiple data types to return,
        // effectively combining multiple data queries into one call.
        // This example demonstrates aggregating only one data type.
        .aggregate(DataType.AGGREGATE_STEP_COUNT_DELTA)
        // Analogous to a "Group By" in SQL, defines how data should be
        // aggregated.
        // bucketByTime allows for a time span, while bucketBySession allows
        // bucketing by sessions.
        .bucketByTime(1, TimeUnit.DAYS)
        .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

مثال قبلی از نقاط داده جمع‌آوری شده استفاده می‌کند، جایی که هر DataPoint نشان‌دهنده تعداد قدم‌های طی شده در یک روز است. برای این مورد خاص، نقاط داده انبوه دو مزیت دارند:

  • برنامه شما و فروشگاه تناسب اندام مقادیر کمتری از داده ها را مبادله می کنند.
  • برنامه شما مجبور نیست داده ها را به صورت دستی جمع آوری کند.

جمع آوری داده ها برای انواع فعالیت های متعدد

برنامه شما می تواند از درخواست های داده برای بازیابی انواع مختلف داده استفاده کند. مثال زیر نحوه ایجاد DataReadRequest برای دریافت کالری سوزانده شده برای هر فعالیت انجام شده در محدوده زمانی مشخص را نشان می دهد. داده‌های به‌دست‌آمده با کالری در هر فعالیت مطابق با برنامه Google Fit گزارش شده است، جایی که هر فعالیت سطل اطلاعات کالری خود را دریافت می‌کند.

کاتلین

val readRequest = DataReadRequest.Builder()
    .aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
    .bucketByActivityType(1, TimeUnit.SECONDS)
    .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .build()

جاوا

DataReadRequest readRequest = new DataReadRequest.Builder()
        .aggregate(DataType.AGGREGATE_CALORIES_EXPENDED)
        .bucketByActivityType(1, TimeUnit.SECONDS)
        .setTimeRange(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

پس از ایجاد یک نمونه DataReadRequest ، از روش HistoryClient.readData() برای خواندن ناهمزمان داده های تاریخی استفاده کنید.

مثال زیر نحوه بدست آوردن نمونه های DataPoint را از DataSet نشان می دهد:

کاتلین

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .readData(readRequest)
    .addOnSuccessListener { response ->
        // The aggregate query puts datasets into buckets, so flatten into a
        // single list of datasets
        for (dataSet in response.buckets.flatMap { it.dataSets }) {
            dumpDataSet(dataSet)
        }
    }
    .addOnFailureListener { e ->
        Log.w(TAG,"There was an error reading data from Google Fit", e)
    }

fun dumpDataSet(dataSet: DataSet) {
    Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}")
    for (dp in dataSet.dataPoints) {
        Log.i(TAG,"Data point:")
        Log.i(TAG,"\tType: ${dp.dataType.name}")
        Log.i(TAG,"\tStart: ${dp.getStartTimeString()}")
        Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}")
        for (field in dp.dataType.fields) {
            Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}")
        }
    }
}

fun DataPoint.getStartTimeString() = Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime().toString()

fun DataPoint.getEndTimeString() = Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
    .atZone(ZoneId.systemDefault())
    .toLocalDateTime().toString()

جاوا

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .readData(readRequest)
        .addOnSuccessListener (response -> {
            // The aggregate query puts datasets into buckets, so convert to a
            // single list of datasets
            for (Bucket bucket : response.getBuckets()) {
                for (DataSet dataSet : bucket.getDataSets()) {
                    dumpDataSet(dataSet);
                }
            }
        })
        .addOnFailureListener(e ->
            Log.w(TAG, "There was an error reading data from Google Fit", e));

}

private void dumpDataSet(DataSet dataSet) {
    Log.i(TAG, "Data returned for Data type: ${dataSet.dataType.name}");
    for (DataPoint dp : dataSet.getDataPoints()) {
        Log.i(TAG,"Data point:");
        Log.i(TAG,"\tType: ${dp.dataType.name}");
        Log.i(TAG,"\tStart: ${dp.getStartTimeString()}");
        Log.i(TAG,"\tEnd: ${dp.getEndTimeString()}");
        for (Field field : dp.getDataType().getFields()) {
            Log.i(TAG,"\tField: ${field.name.toString()} Value: ${dp.getValue(field)}");
        }
    }
}


private String getStartTimeString() {
    return Instant.ofEpochSecond(this.getStartTime(TimeUnit.SECONDS))
        .atZone(ZoneId.systemDefault())
        .toLocalDateTime().toString();
}

private String getEndTimeString() {
    return Instant.ofEpochSecond(this.getEndTime(TimeUnit.SECONDS))
            .atZone(ZoneId.systemDefault())
            .toLocalDateTime().toString();
}

کل داده های روزانه را بخوانید

Google Fit همچنین دسترسی ساده به کل روزانه یک نوع داده مشخص را فراهم می کند. از روش HistoryClient.readDailyTotal() برای بازیابی نوع داده ای که از نیمه شب روز جاری در منطقه زمانی فعلی دستگاه تعیین می کنید، استفاده کنید. برای مثال، نوع داده TYPE_STEP_COUNT_DELTA را به این روش ارسال کنید تا کل مراحل روزانه را بازیابی کنید. می توانید یک نوع داده آنی را که دارای مجموع کل روزانه است، ارسال کنید. برای اطلاعات بیشتر در مورد انواع داده های پشتیبانی شده، به DataType.getAggregateType مراجعه کنید.

وقتی این روش با استفاده از حساب پیش‌فرض فراخوانی می‌شود و هیچ محدوده‌ای مشخص نشده است، Google Fit برای اشتراک در به‌روزرسانی‌های TYPE_STEP_COUNT_DELTA از روش HistoryClient.readDailyTotal() نیازی به مجوز ندارد. اگر برای استفاده در مناطقی که نمی‌توانید پانل مجوزها را نشان دهید، به عنوان مثال در صفحه‌های ساعت Wear OS، به داده‌های مرحله نیاز دارید، این می‌تواند مفید باشد.

کاربران ترجیح می‌دهند تعداد گام‌های ثابتی را در برنامه Google Fit، سایر برنامه‌ها و صفحه‌های ساعت Wear OS ببینند، زیرا این کار تجربه‌ای ثابت و قابل اعتماد را برای آنها فراهم می‌کند. برای ثابت نگه داشتن تعداد گام‌ها، از برنامه یا صفحه ساعت خود در مراحل پلتفرم Google Fit مشترک شوید و سپس تعداد آن‌ها را در onExitAmbient() به‌روزرسانی کنید. برای اطلاعات بیشتر در مورد نحوه استفاده از این داده ها در صفحه ساعت، به عوارض Watch Face و نمونه برنامه Android WatchFace مراجعه کنید.

درج داده ها

برای درج داده های تاریخی، ابتدا یک نمونه DataSet ایجاد کنید:

کاتلین

// Declare that the data being inserted was collected during the past hour.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusHours(1)

// Create a data source
val dataSource = DataSource.Builder()
    .setAppPackageName(this)
    .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
    .setStreamName("$TAG - step count")
    .setType(DataSource.TYPE_RAW)
    .build()

// For each data point, specify a start time, end time, and the
// data value -- in this case, 950 new steps.
val stepCountDelta = 950
val dataPoint =
    DataPoint.builder(dataSource)
        .setField(Field.FIELD_STEPS, stepCountDelta)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build()

val dataSet = DataSet.builder(dataSource)
    .add(dataPoint)
    .build()

جاوا

// Declare that the data being inserted was collected during the past hour.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusHours(1);

// Create a data source
DataSource dataSource = new DataSource.Builder()
        .setAppPackageName(this)
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .setStreamName("$TAG - step count")
        .setType(DataSource.TYPE_RAW)
        .build();

// For each data point, specify a start time, end time, and the
// data value -- in this case, 950 new steps.
int stepCountDelta = 950;
DataPoint dataPoint = DataPoint.builder(dataSource)
        .setField(Field.FIELD_STEPS, stepCountDelta)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

DataSet dataSet = DataSet.builder(dataSource)
        .add(dataPoint)
        .build();

پس از ایجاد یک نمونه DataSet ، از روش HistoryClient.insertData برای افزودن ناهمزمان این داده های تاریخی استفاده کنید.

کاتلین

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .insertData(dataSet)
    .addOnSuccessListener {
        Log.i(TAG, "DataSet added successfully!")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "There was an error adding the DataSet", e)
    }

جاوا

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .insertData(dataSet)
        .addOnSuccessListener (unused ->
                Log.i(TAG, "DataSet added successfully!"))
        .addOnFailureListener(e ->
                Log.w(TAG, "There was an error adding the DataSet", e));
}

نقاط داده متناقض را مدیریت کنید

هر DataPoint در DataSet برنامه شما باید یک startTime و یک endTime داشته باشد که یک بازه زمانی منحصر به فرد را در آن DataSet تعریف کند، بدون همپوشانی بین نمونه های DataPoint .

اگر برنامه شما تلاش کند یک DataPoint جدید وارد کند که با نمونه DataPoint موجود در تضاد است، DataPoint جدید کنار گذاشته می‌شود. برای درج یک DataPoint جدید که ممکن است با نقاط داده موجود همپوشانی داشته باشد، از روش HistoryClient.updateData که در Update data توضیح داده شده است استفاده کنید.

اگر مدت زمان آن با نقاط داده موجود همپوشانی داشته باشد، نمی‌توان یک نقطه داده را درج کرد

شکل 1. روش insertData() چگونه نقاط داده جدیدی را مدیریت می کند که با یک DataPoint موجود در تضاد هستند.

به روز رسانی داده ها

Google Fit به برنامه شما اجازه می‌دهد تا داده‌های سلامتی و سلامتی را که قبلاً درج کرده بود، به‌روزرسانی کند. برای افزودن داده‌های تاریخی برای یک DataSet جدید، یا برای افزودن نمونه‌های DataPoint جدید که با نقاط داده موجود تضاد ندارند، از روش HistoryApi.insertData استفاده کنید.

برای به روز رسانی داده های تاریخی، از روش HistoryClient.updateData استفاده کنید. این روش تمام نمونه های DataPoint موجود را که با نمونه های DataPoint اضافه شده با استفاده از این روش همپوشانی دارند، حذف می کند.

برای به‌روزرسانی داده‌های سلامت و تندرستی تاریخی، ابتدا یک نمونه DataSet ایجاد کنید:

کاتلین

// Declare that the historical data was collected during the past 50 minutes.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusMinutes(50)

// Create a data source
val dataSource  = DataSource.Builder()
    .setAppPackageName(this)
    .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
    .setStreamName("$TAG - step count")
    .setType(DataSource.TYPE_RAW)
    .build()

// Create a data set
// For each data point, specify a start time, end time, and the
// data value -- in this case, 1000 new steps.
val stepCountDelta = 1000

val dataPoint = DataPoint.builder(dataSource)
    .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .setField(Field.FIELD_STEPS, stepCountDelta)
    .build()

val dataSet = DataSet.builder(dataSource)
    .add(dataPoint)
    .build()

جاوا

// Declare that the historical data was collected during the past 50 minutes.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusMinutes(50);

// Create a data source
DataSource dataSource = new DataSource.Builder()
        .setAppPackageName(this)
        .setDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .setStreamName("$TAG - step count")
        .setType(DataSource.TYPE_RAW)
        .build();

// Create a data set
// For each data point, specify a start time, end time, and the
// data value -- in this case, 1000 new steps.
int stepCountDelta = 1000;

DataPoint dataPoint = DataPoint.builder(dataSource)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .setField(Field.FIELD_STEPS, stepCountDelta)
        .build();

DataSet dataSet = DataSet.builder(dataSource)
        .add(dataPoint)
        .build();

سپس از DataUpdateRequest.Builder() برای ایجاد یک درخواست به روز رسانی داده جدید استفاده کنید و از متد HistoryClient.updateData برای ایجاد درخواست استفاده کنید:

کاتلین

val request = DataUpdateRequest.Builder()
    .setDataSet(dataSet)
    .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .build()

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .updateData(request)
    .addOnSuccessListener {
        Log.i(TAG, "DataSet updated successfully!")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "There was an error updating the DataSet", e)
    }

جاوا

DataUpdateRequest request = new DataUpdateRequest.Builder()
        .setDataSet(dataSet)
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .build();

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .updateData(request)
        .addOnSuccessListener(unused ->
                Log.i(TAG, "DataSet updated successfully!"))
        .addOnFailureListener(e ->
                Log.w(TAG, "There was an error updating the DataSet", e));

داده ها را حذف کنید

Google Fit به برنامه شما اجازه می‌دهد داده‌های سابقه سلامت و تندرستی را که قبلاً درج کرده است حذف کند.

برای حذف داده های تاریخی، از روش HistoryClient.deleteData استفاده کنید:

کاتلین

// Declare that this code deletes step count information that was collected
// throughout the past day.
val endTime = LocalDateTime.now().atZone(ZoneId.systemDefault())
val startTime = endTime.minusDays(1)

// Create a delete request object, providing a data type and a time interval
val request = DataDeleteRequest.Builder()
    .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
    .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
    .build()

// Invoke the History API with the HistoryClient object and delete request, and
// then specify a callback that will check the result.
Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .deleteData(request)
    .addOnSuccessListener {
        Log.i(TAG, "Data deleted successfully!")
    }
    .addOnFailureListener { e ->
        Log.w(TAG, "There was an error with the deletion request", e)
    }

جاوا

// Declare that this code deletes step count information that was collected
// throughout the past day.
ZonedDateTime endTime = LocalDateTime.now().atZone(ZoneId.systemDefault());
ZonedDateTime startTime = endTime.minusDays(1);

// Create a delete request object, providing a data type and a time interval
DataDeleteRequest request = new DataDeleteRequest.Builder()
        .setTimeInterval(startTime.toEpochSecond(), endTime.toEpochSecond(), TimeUnit.SECONDS)
        .addDataType(DataType.TYPE_STEP_COUNT_DELTA)
        .build();

// Invoke the History API with the HistoryClient object and delete request, and
// then specify a callback that will check the result.
Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .deleteData(request)
        .addOnSuccessListener (unused ->
                Log.i(TAG, "Data deleted successfully!"))
        .addOnFailureListener(e ->
        Log.w(TAG, "There was an error with the deletion request", e));

برنامه ها می توانند داده ها را از جلسات خاص حذف کنند یا همه داده ها را حذف کنند. برای اطلاعات بیشتر، به مرجع API برای DataDeleteRequest مراجعه کنید.

برای به روز رسانی داده ها ثبت نام کنید

برنامه شما می‌تواند با ثبت‌نام در SensorsClient ، داده‌های خام حسگر را در زمان واقعی بخواند.

برای انواع دیگر داده‌هایی که تعداد کمتری دارند و به‌صورت دستی شمارش می‌شوند، برنامه شما می‌تواند برای دریافت به‌روزرسانی‌ها ثبت نام کند، وقتی این اندازه‌گیری‌ها در پایگاه داده Google Fit درج می‌شوند. نمونه هایی از این نوع داده ها شامل قد، وزن و تمریناتی مانند وزنه برداری است. برای جزئیات بیشتر، لیست کامل انواع داده های پشتیبانی شده را ببینید. برای ثبت نام برای به روز رسانی، از HistoryClient.registerDataUpdateListener استفاده کنید.

قطعه کد زیر به برنامه اجازه می دهد زمانی که کاربر مقدار جدیدی را برای وزن خود وارد می کند مطلع شود:

کاتلین

val intent = Intent(this, MyDataUpdateService::class.java)
val pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

val request = DataUpdateListenerRegistrationRequest.Builder()
    .setDataType(DataType.TYPE_WEIGHT)
    .setPendingIntent(pendingIntent)
    .build()

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
    .registerDataUpdateListener(request)
    .addOnSuccessListener {
        Log.i(TAG, "DataUpdateListener registered")
    }

جاوا

Intent intent = new Intent(this, MyDataUpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

DataUpdateListenerRegistrationRequest request = new DataUpdateListenerRegistrationRequest.Builder()
        .setDataType(DataType.TYPE_WEIGHT)
        .setPendingIntent(pendingIntent)
        .build();

Fitness.getHistoryClient(this, GoogleSignIn.getAccountForExtension(this, fitnessOptions))
        .registerDataUpdateListener(request)
        .addOnSuccessListener(unused ->
                Log.i(TAG, "DataUpdateListener registered"));

برای دریافت اعلان‌های به‌روزرسانی‌ها می‌توان از IntentService استفاده کرد:

کاتلین

class MyDataUpdateService : IntentService("MyDataUpdateService") {
    override fun onHandleIntent(intent: Intent?) {
        val update = DataUpdateNotification.getDataUpdateNotification(intent)
        // Show the time interval over which the data points were collected.
        // To extract specific data values, in this case the user's weight,
        // use DataReadRequest.
        update?.apply {
            val start = getUpdateStartTime(TimeUnit.MILLISECONDS)
            val end = getUpdateEndTime(TimeUnit.MILLISECONDS)

            Log.i(TAG, "Data Update start: $start end: $end DataType: ${dataType.name}")
        }
    }
}

جاوا

public class MyDataUpdateService extends IntentService {

    public MyDataUpdateService(String name) {
        super("MyDataUpdateService");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (intent != null) {
            DataUpdateNotification update = DataUpdateNotification.getDataUpdateNotification(intent);

            // Show the time interval over which the data points
            // were collected.
            // To extract specific data values, in this case the user's weight,
            // use DataReadRequest.
            if (update != null) {
                long start = update.getUpdateStartTime(TimeUnit.MILLISECONDS);
                long end = update.getUpdateEndTime(TimeUnit.MILLISECONDS);
            }

            Log.i(TAG, "Data Update start: $start end: $end DataType: ${dataType.name}");
        }
    }
}

IntentService باید در فایل AndroidManifest.xml شما اعلان شود.