Android Studio中设备使用时长追踪功能无法正常工作
数字健康应用使用时长追踪失效问题排查与修复
问题背景
开发一款数字健康应用,需统计设备解锁事件并显示使用时长。已通过Receiver和Service实现解锁事件统计,但使用时长追踪功能失效。
实现逻辑:屏幕点亮时记录当前时间,屏幕熄灭时计算时长差并累加总时长,最终以00:00:00格式显示在TextView中(初始值为法语"0h et 0min")。
问题现象:运行代码后,TextView值变为00:00:00,但后续无论使用设备多久,数值始终保持不变。
相关代码
UserPageActivity代码
public class UserPageActivity extends AppCompatActivity { private static final int PERMISSION_REQUEST_CODE=123; private deverouillageReceiver devReceiver; private TempsUseReceiver tempsUseReceiver; @Override protected void onCreate(Bundle savedInstanceState) { //super and view binding here if (ActivityCompat.checkSelfPermission(this, Manifest.permission.RECEIVE_BOOT_COMPLETED) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECEIVE_BOOT_COMPLETED}, PERMISSION_REQUEST_CODE); } else { registerTempsUseReceiver(); } startTempsUseService(); } private void registerTempsUseReceiver(){ tempsUseReceiver =new TempsUseReceiver(); registerReceiver(tempsUseReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON)); registerReceiver(tempsUseReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); } @Override public void onRequestPermissionsResult(int requestCode, String[] permission, int[] grantResult) { super.onRequestPermissionsResult(requestCode, permission, grantResult); if (requestCode == PERMISSION_REQUEST_CODE) { if (grantResult.length > 0 && grantResult[0] == PackageManager.PERMISSION_GRANTED) { registerUnlockReceiver(); registerTempsUseReceiver(); } } } @Override protected void onResume() { super.onResume(); BroadcastReceiver updateTotalTimeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("com.example.digitalbalance1.STOP_TIME")) { long totalTime = intent.getLongExtra("totalTime", 0); updateTotalTimeTextView(totalTime); } } }; IntentFilter updateTotalTimeFilter = new IntentFilter("com.example.digitalbalance1.UPDATE_TOTAL_TIME"); registerReceiver(updateTotalTimeReceiver, updateTotalTimeFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(tempsUseReceiver); } private void updateTotalTimeTextView(long totalTime){ ui.useEdit.setText(convertMillisecondsToTime(totalTime)); } public String convertMillisecondsToTime(long milliseconds) { long seconds = TimeUnit.MILLISECONDS.toSeconds(milliseconds) % 60; long minutes = TimeUnit.MILLISECONDS.toMinutes(milliseconds) % 60; long hours = TimeUnit.MILLISECONDS.toHours(milliseconds); return String.format("%02d:%02d:%02d", hours, minutes, seconds); } private void startTempsUseService(){ Intent serviceIntent = new Intent(this, TempsUseService.class); if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.O ){ startForegroundService(serviceIntent); } else { startService(serviceIntent); } } }
TempsUseReceiver代码
public class TempsUseReceiver extends BroadcastReceiver { private long countTime = 0; private long totalTime = 0; private boolean screenOn = true; //private static final String ACTION_COUNT_TIME = "com.example.digitalbalance1.COUNT_TIME"; private static final String ACTION_STOP_TIME = "com.example.digitalbalance1.STOP_TIME"; @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if(action.equals(Intent.ACTION_SCREEN_ON)) { Intent startCount = new Intent(context, TempsUseService.class); startCount.setAction(ACTION_STOP_TIME); context.startService(startCount); countTime=System.currentTimeMillis(); System.out.println("1"+countTime); screenOn=true; } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { if(screenOn){ Intent endCount = new Intent(context, TempsUseService.class); endCount.setAction(ACTION_STOP_TIME); context.startService(endCount); long endCountTime = System.currentTimeMillis(); System.out.println("2"+endCountTime); long sessionTime = endCountTime-countTime; totalTime += sessionTime; updateTotalTime(context, totalTime); } screenOn=false; } } public long getCountTime(){return countTime;} public long getTotalTime(Context context){ //return totalTime value that is in the file } }
TempsUseService代码
public class TempsUseService extends Service { private TempsUseReceiver tempsUseReceiver; private static final int NOTIFICATION_ID = 2; private static final String CHANNEL_ID = "ForegroundServiceChannel"; private long totalTime = 0; public void onCreate(){ super.onCreate(); tempsUseReceiver=new TempsUseReceiver(); registerTempsUseReceiver(); startForeground(NOTIFICATION_ID, createNotification()); } public int onStartCommand(Intent intent, int flags, int startId){ if(intent!=null && intent.getAction()!=null && intent.getAction().equals("com.example.digitalbalance1.STOP_TIME")){ long countTime= tempsUseReceiver.getCountTime(); long currentTime = System.currentTimeMillis(); long sessionTime = currentTime - countTime; totalTime += sessionTime; updateTotalTime(this, totalTime); } else if (intent!=null && intent.getAction()!=null && intent.getAction().equals("com.example.digitalbalance1.UPDATE_TOTAL_TIME")) { long updatedTotalTime = intent.getLongExtra("totalTime", 0); if (updatedTotalTime != 0) { totalTime = updatedTotalTime; } } updateTotalTimeInUserPageActivity(totalTime); return START_STICKY; } private void updateTotalTimeInUserPageActivity(long totalTime) { Intent updateIntent = new Intent("com.example.digitalbalance1.STOP_TIME"); updateIntent.putExtra("totalTime", totalTime); sendBroadcast(updateIntent); } private void registerTempsUseReceiver(){ registerReceiver(tempsUseReceiver, new IntentFilter(Intent.ACTION_SCREEN_ON)); registerReceiver(tempsUseReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); } private void unregisterUnlockReceiver(){ unregisterReceiver(tempsUseReceiver); } public void onDestroy(){ super.onDestroy(); unregisterUnlockReceiver(); } private Notification createNotification() { //Notif creation } private void updateTotalTime(Context context, long totalTime) { //write totalTime value in its file Intent("com.example.digitalbalance1.UPDATE_TOTAL_TIME"); updateIntent.putExtra("totalTime", totalTime); context.sendBroadcast(updateIntent); } private File getTotalTimeFile(Context context){ //retrieve totalTime value from its file } } }
问题分析
- 重复注册导致实例不一致:
UserPageActivity和TempsUseService各自注册了TempsUseReceiver,屏幕事件会被两个独立实例接收。Service中调用getCountTime()时,拿到的是自身创建的Receiver实例的初始值0,计算出的时长差无效,总时长无法累加。 - 广播Action不匹配:
UserPageActivity注册的广播过滤器监听UPDATE_TOTAL_TIME,但接收逻辑判断的是STOP_TIME,导致UI无法接收到更新广播。 - Receiver状态无法持久化:广播接收器可能被系统回收重建,其内部的
countTime、totalTime等变量会重置,丢失之前的统计数据。 - 逻辑重复冲突:Receiver和Service同时处理时长计算,既在Receiver中累加总时长,又调用Service重复计算,导致逻辑混乱。
修复方案
1. 移除重复注册,统一由Service处理事件
删除UserPageActivity中的registerTempsUseReceiver()调用及相关代码,仅保留Service注册和处理屏幕广播,确保只有一个TempsUseReceiver实例运行。
2. 修正广播Action匹配问题
调整UserPageActivity的广播接收逻辑,保持发送与接收的Action一致:
@Override protected void onResume() { super.onResume(); updateTotalTimeReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals("com.example.digitalbalance1.UPDATE_TOTAL_TIME")) { long totalTime = intent.getLongExtra("totalTime", 0); updateTotalTimeTextView(totalTime); } } }; IntentFilter updateTotalTimeFilter = new IntentFilter("com.example.digitalbalance1.UPDATE_TOTAL_TIME"); registerReceiver(updateTotalTimeReceiver, updateTotalTimeFilter); }
3. 持久化统计数据
使用SharedPreferences存储countTime和totalTime,避免Receiver实例重建丢失数据:
// 修改TempsUseReceiver的onReceive逻辑 @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Intent serviceIntent = new Intent(context, TempsUseService.class); if(action.equals(Intent.ACTION_SCREEN_ON)) { serviceIntent.setAction("com.example.digitalbalance1.START_TIME"); context.startService(serviceIntent); } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { serviceIntent.setAction("com.example.digitalbalance1.STOP_TIME"); context.startService(serviceIntent); } }
4. 统一时长计算入口
在Service中处理所有时长统计逻辑:
public int onStartCommand(Intent intent, int flags, int startId){ SharedPreferences sp = getSharedPreferences("TimeTracker", MODE_PRIVATE); if(intent!=null && intent.getAction()!=null){ switch (intent.getAction()){ case "com.example.digitalbalance1.START_TIME": sp.edit().putLong("countTime", System.currentTimeMillis()).apply(); break; case "com.example.digitalbalance1.STOP_TIME": long countTime = sp.getLong("countTime", 0); if(countTime !=0){ long sessionTime = System.currentTimeMillis() - countTime; long totalTime = sp.getLong("totalTime", 0) + sessionTime; sp.edit().putLong("totalTime", totalTime).apply(); updateTotalTime(this, totalTime); } break; } } long currentTotal = sp.getLong("totalTime", 0); updateTotalTimeInUserPageActivity(currentTotal); return START_STICKY; }
5. 修复内存泄漏
在UserPageActivity的onPause中注销临时广播接收器:
private BroadcastReceiver updateTotalTimeReceiver; @Override protected void onPause() { super.onPause(); if(updateTotalTimeReceiver != null){ unregisterReceiver(updateTotalTimeReceiver); } }
内容的提问来源于stack exchange,提问作者Sakura
相关产品推荐
相关产品推荐

