ใช้งานข้อมูลย้อนหลัง

History API ช่วยให้แอปดำเนินการแบบกลุ่มใน Store การออกกำลังกายได้ ทั้งการอ่าน แทรก อัปเดต และลบข้อมูลประวัติด้านสุขภาพและความแข็งแรงสมบูรณ์ ใช้ History API เพื่อทำสิ่งต่อไปนี้

  • อ่านข้อมูลด้านสุขภาพและความแข็งแรงสมบูรณ์ที่แทรกหรือบันทึกโดยใช้แอปอื่นๆ
  • นำเข้าข้อมูลแบบกลุ่มไปยัง Google Fit
  • อัปเดตข้อมูลใน Google Fit
  • ลบข้อมูลย้อนหลังที่แอปของคุณจัดเก็บไว้ก่อนหน้านี้

หากต้องการแทรกข้อมูลที่มีข้อมูลเมตาของเซสชัน ให้ใช้ Sessions API

อ่านข้อมูล

ส่วนต่อไปนี้อธิบายวิธีอ่านข้อมูลรวมประเภทต่างๆ

อ่านข้อมูลรายละเอียดและรวบรวมข้อมูล

หากต้องการอ่านข้อมูลย้อนหลัง ให้สร้างอินสแตนซ์ DataReadRequest

Kotlin

// 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()

Java

// 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 แต่ละรายการแสดงถึงจำนวนก้าวที่เดินในแต่ละวัน สำหรับกรณีการใช้งานนี้ จุดข้อมูลรวมมีข้อดี 2 ประการ ได้แก่

  • แอปและร้านค้าฟิตเนสจะแลกเปลี่ยนข้อมูลปริมาณน้อยกว่า
  • แอปของคุณไม่จําเป็นต้องรวบรวมข้อมูลด้วยตนเอง

รวมข้อมูลสำหรับกิจกรรมหลายประเภท

แอปของคุณสามารถใช้คําขอข้อมูลเพื่อดึงข้อมูลประเภทต่างๆ ได้ ตัวอย่างต่อไปนี้แสดงวิธีสร้าง DataReadRequest เพื่อดูแคลอรี่ที่ใช้ไปสำหรับแต่ละกิจกรรมที่ทำภายในช่วงเวลาที่ระบุ ข้อมูลที่ได้จากกิจกรรมจะตรงกับแคลอรีต่อกิจกรรมตามที่รายงานไว้ในแอป Google Fit ซึ่งแต่ละกิจกรรมจะได้รับที่เก็บข้อมูลแคลอรี่ของตัวเอง

Kotlin

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

Java

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

Kotlin

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()

Java

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() ดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีใช้ข้อมูลนี้ในหน้าปัดได้ที่ข้อมูลแทรกของหน้าปัดและแอปพลิเคชันตัวอย่าง Android WatchFace

แทรกข้อมูล

หากต้องการแทรกข้อมูลย้อนหลัง ให้สร้างอินสแตนซ์ DataSet ก่อน

Kotlin

// 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()

Java

// 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 เพื่อเพิ่มข้อมูลย้อนหลังนี้แบบไม่พร้อมกัน

Kotlin

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)
    }

Java

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 ที่อธิบายไว้ในส่วนอัปเดตข้อมูล

แทรกจุดข้อมูลไม่ได้หากระยะเวลาของจุดทับซ้อนกับจุดข้อมูลที่มีอยู่

รูปที่ 1 วิธีที่เมธอด insertData() จัดการจุดข้อมูลใหม่ที่ขัดแย้งกับ DataPoint ที่มีอยู่

อัปเดตข้อมูล

Google Fit ช่วยให้แอปอัปเดตข้อมูลสุขภาพและความแข็งแรงสมบูรณ์ที่เคยแทรกไว้ก่อนหน้านี้ได้ หากต้องการเพิ่มข้อมูลย้อนหลังสำหรับ DataSet ใหม่ หรือเพิ่ม DataPoint อินสแตนซ์ใหม่ที่ไม่ขัดแย้งกับจุดข้อมูลที่มีอยู่ ให้ใช้เมธอด HistoryApi.insertData

หากต้องการอัปเดตข้อมูลย้อนหลัง ให้ใช้เมธอด HistoryClient.updateData วิธีนี้จะลบอินสแตนซ์ DataPoint ที่มีอยู่ซึ่งซ้อนทับกับอินสแตนซ์ DataPoint ที่เพิ่มโดยใช้เมธอดนี้

หากต้องการอัปเดตข้อมูลด้านสุขภาพและความแข็งแรงสมบูรณ์ในอดีต ให้สร้างอินสแตนซ์ DataSet ก่อน:

Kotlin

// 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()

Java

// 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 ในการสร้างคำขอ ดังนี้

Kotlin

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)
    }

Java

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 ดังนี้

Kotlin

// 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)
    }

Java

// 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

ข้อมูลโค้ดต่อไปนี้ช่วยให้แอปได้รับแจ้งเมื่อผู้ใช้ป้อนค่าใหม่สำหรับน้ำหนัก

Kotlin

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")
    }

Java

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 เพื่อรับการแจ้งเตือนการอัปเดตได้โดยทำดังนี้

Kotlin

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}")
        }
    }
}

Java

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