Android Kotlin:Activity Recognition API注册成功但无transitionEvents更新求助
我是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

