You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Kotlin:Activity Recognition API注册成功但无transitionEvents更新求助

问题:Activity Recognition API无法接收活动切换事件

我是Android开发新手,正在开发一款用户活动检测应用,使用Google Play Services Activity Recognition API,严格遵循官方示例开发。应用客户端运行正常,requestActivityTransitionUpdates()已成功完成API注册,但始终无法获取用户活动切换事件,ActivityTransitionResult.extractResult(intent).transitionEvents()返回结果一直为空。Sleep API也存在同样问题,求排查解决。


Receiver.kt

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.example.hendi.util.ActivityTransitionUtil
import com.google.android.gms.location.ActivityTransitionResult

class ActivityTransitionReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Log.d("MyShedCheck", "onReceive()")
        if (ActivityTransitionResult.hasResult(intent)) {
            val result = ActivityTransitionResult.extractResult(intent)
            result?.let {
                result.transitionEvents.forEach { event ->
                    Log.d("RECEIVER", "Activity Detected:" +
                            "\ncontext: ${context}" +
                            "\nintent:${intent}" +
                            "\ntype: ${ActivityTransitionUtil.toActivityString(event.activityType)}"
                    )
                }
            }
        }
    }
}

MainActivity.kt

import android.annotation.SuppressLint
import android.app.PendingIntent
import android.content.Intent
import android.content.SharedPreferences
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.preference.PreferenceManager
import com.example.hendi.receiver.ActivityTransitionReceiver
import com.example.hendi.ui.theme.HendiTheme
import com.example.hendi.util.ActivityTransitionUtil
import com.example.hendi.util.Constants
import com.example.hendi.util.PermissionState
import com.google.android.gms.location.ActivityRecognition
import com.google.android.gms.location.ActivityRecognitionClient

class MainActivity : ComponentActivity() {

    lateinit var client: ActivityRecognitionClient
    lateinit var storage: SharedPreferences

    companion object {
        const val TAG = "MyShedCheck"
    }

    @RequiresApi(Build.VERSION_CODES.S)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        client = ActivityRecognition.getClient(this)
        storage = PreferenceManager.getDefaultSharedPreferences(this)

        var isChecked = false
        val permissionState = PermissionState(this) {
            if (it.permissionGranted) {
                isChecked = true
                requestForUpdates()
            } else {
                isChecked = false
                it.requestPermission()
            }
        }

        setContent {
            HendiTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    ToggleAction(permissionState = permissionState,
                        request = { requestForUpdates() },
                        remove = { deregisterForUpdates() })
                }
            }
        }
    }

    @RequiresApi(Build.VERSION_CODES.S)
    @SuppressLint("MissingPermission")
    fun requestForUpdates() {
        client
            .requestActivityTransitionUpdates(
                ActivityTransitionUtil.getActivityTransitionRequest(),
                getPendingIntent()
            )
            .addOnSuccessListener {
                Log.d(TAG, "Successful registration")
            }
            .addOnFailureListener {
                Log.d(TAG,"Unsuccessful registration:$it")
            }
    }

    @SuppressLint("MissingPermission")
    @RequiresApi(Build.VERSION_CODES.S)
    fun deregisterForUpdates() {
        client
            .removeActivityTransitionUpdates(getPendingIntent())
            .addOnSuccessListener {
                Log.d(TAG, "Successful deregistration")
            }
            .addOnFailureListener {
                Log.d(TAG,"Unsuccessful deregistration:$it")
            }
    }

    @RequiresApi(Build.VERSION_CODES.S)
    private fun getPendingIntent() :PendingIntent {
        val intent = Intent(this, ActivityTransitionReceiver::class.java)
        return PendingIntent.getBroadcast(
            this,
            122,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
        )
    }

}

@Composable
fun ToggleAction(permissionState: PermissionState, request: () -> Unit, remove: () -> Unit) {

    Log.d("MyShedCheck", "Permission: ${permissionState.permissionGranted}")
    var isChecked by remember { mutableStateOf(
        if (permissionState.permissionGranted) {
            true
        } else {
            permissionState.requestPermission()
            false
        }
    )
    }
    Switch(checked = isChecked,
        onCheckedChange = {
            isChecked = it

            if (it) {
                request()
            } else {
                remove()
            }
        }
    )
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Required for 28 and below. -->
    <uses-permission android:name="com.google.android.gms.permission.ACTIVITY_RECOGNITION" />
    <!-- Required for 29+. -->
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />

    <application
        android:name=".App"
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/Theme.Tracker"
        tools:targetApi="31">
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@style/Theme.Tracker">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <receiver
            android:name=".receiver.ActivityTransitionReceiver"
            android:exported="false"
            android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION">
            <intent-filter>
                <action android:name="action.TRANSITIONS_DATA" />
            </intent-filter>
        </receiver>
    </application>

</manifest>

ActivityTransitionUtil.kt

import android.content.Context
import com.google.android.gms.location.ActivityTransition
import com.google.android.gms.location.ActivityTransitionRequest
import com.google.android.gms.location.DetectedActivity

object ActivityTransitionUtil {
    private fun getTransitions(): MutableList<ActivityTransition> {
        val transitions = mutableListOf<ActivityTransition>()

        transitions.add(
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.WALKING)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                .build()
        )
        transitions.add(
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.WALKING)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                .build()
        )

        transitions.add(
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.STILL)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_ENTER)
                .build()
        )
        transitions.add(
            ActivityTransition.Builder()
                .setActivityType(DetectedActivity.STILL)
                .setActivityTransition(ActivityTransition.ACTIVITY_TRANSITION_EXIT)
                .build()
        )

        return transitions
    }

    fun getActivityTransitionRequest() = ActivityTransitionRequest(getTransitions())

    fun hasActivityTransitionPermissions(context: Context): Boolean =
        context.hasPermission()

    fun toActivityString(activity: Int): String {
        return when (activity) {
            DetectedActivity.STILL -> "STILL"
            DetectedActivity.WALKING -> "WAKING"
            else -> "UNKNOWN"
        }
    }

    fun toTransitionType(transitionType: Int): String {
        return when (transitionType) {
            ActivityTransition.ACTIVITY_TRANSITION_ENTER -> "ENTER"
            ActivityTransition.ACTIVITY_TRANSITION_EXIT -> "EXIT"
            else -> "UNKNOWN"
        }
    }
}

App.kt

import android.app.Application

class App : Application() {
    override fun onCreate() {
        super.onCreate()
    }
}

排查与修复方案

1. 修正PendingIntent的Intent Action匹配

你的Manifest中给Receiver配置了action.TRANSITIONS_DATA的Intent Filter,但创建PendingIntent时未给Intent设置该Action,导致系统发送的广播无法匹配到Receiver。

修改MainActivity.kt的getPendingIntent()方法:

@RequiresApi(Build.VERSION_CODES.S)
private fun getPendingIntent() :PendingIntent {
    val intent = Intent(this, ActivityTransitionReceiver::class.java)
    intent.action = "action.TRANSITIONS_DATA" // 添加这一行,与Manifest中的Action一致
    return PendingIntent.getBroadcast(
        this,
        122,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_MUTABLE
    )
}

2. 移除Receiver的冗余权限配置

Manifest中给Receiver添加的android:permission="com.google.android.gms.permission.ACTIVITY_RECOGNITION"是多余的,该权限是用于调用Activity Recognition API时的权限,Receiver无需配置,反而可能导致广播被拦截。

修改AndroidManifest.xml中的Receiver配置:

<receiver
    android:name=".receiver.ActivityTransitionReceiver"
    android:exported="false">
    <intent-filter>
        <action android:name="action.TRANSITIONS_DATA" />
    </intent-filter>
</receiver>

3. 修正活动类型字符串笔误(非功能性问题)

ActivityTransitionUtil.kt中DetectedActivity.WALKING对应的字符串写成了"WAKING",建议修正为正确的"WALKING",避免日志输出错误信息:

fun toActivityString(activity: Int): String {
    return when (activity) {
        DetectedActivity.STILL -> "STILL"
        DetectedActivity.WALKING -> "WALKING" // 修正此处
        else -> "UNKNOWN"
    }
}

4. 测试与环境注意事项

  • 真机测试:模拟器无法模拟真实的传感器活动,建议使用物理设备测试,并且需要真实的状态切换(比如从静止开始行走,或行走后停下)才能触发事件。
  • 系统权限与省电设置:确保设备已授予ACTIVITY_RECOGNITION权限,且未开启严格省电模式(部分厂商的后台限制会阻止Activity Recognition的后台运行)。
  • Sleep API额外要求:Sleep API需要Android 14(API 34)及以上版本,且设备硬件支持睡眠状态检测,同时需确保已申请必要权限。

内容的提问来源于stack exchange,提问作者Ali Vafaei

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 10:05:33