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

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

}

问题分析

  1. 重复注册导致实例不一致:UserPageActivity和TempsUseService各自注册了TempsUseReceiver,屏幕事件会被两个独立实例接收。Service中调用getCountTime()时,拿到的是自身创建的Receiver实例的初始值0,计算出的时长差无效,总时长无法累加。
  2. 广播Action不匹配:UserPageActivity注册的广播过滤器监听UPDATE_TOTAL_TIME,但接收逻辑判断的是STOP_TIME,导致UI无法接收到更新广播。
  3. Receiver状态无法持久化:广播接收器可能被系统回收重建,其内部的countTime、totalTime等变量会重置,丢失之前的统计数据。
  4. 逻辑重复冲突: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 10:58:11