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

如何通过SharedPreferences从Service向Fragment传数据?步数值不更新

问题分析

你的代码核心逻辑问题很清晰:StepsFragment只在点击启动服务的瞬间读取了一次SharedPreferences的值,之后StepsService每秒更新SharedPreferences,但Fragment没有任何机制监听这个变化,所以TextView自然不会自动刷新最新的计步数据。另外,Service里的while(true)线程没有停止逻辑,会导致服务销毁后线程仍在运行,造成内存泄漏,这个也得一起修复。

解决方案

下面给你三种可行的修复方案,从直接适配现有代码到符合现代Android架构的都有:

方案1:监听SharedPreferences变化(最直接适配现有代码)

利用SharedPreferences.OnSharedPreferenceChangeListener接口,让Fragment监听指定键的变化,一旦值更新就同步到TextView。

修改StepsFragment.java:

public class StepsFragment extends Fragment implements SharedPreferences.OnSharedPreferenceChangeListener {
    private TextView tv_steps;
    private SharedPreferences prefs;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.your_fragment_layout, container, false);
        tv_steps = view.findViewById(R.id.your_textview_id);
        // 初始化SharedPreferences
        prefs = getActivity().getSharedPreferences("DeviceToken", MODE_PRIVATE);
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        // 注册监听器
        prefs.registerOnSharedPreferenceChangeListener(this);
        // 首次进入时加载当前存储的计步值
        String currentSteps = prefs.getString("DeviceToken", "0");
        tv_steps.setText(currentSteps);
    }

    @Override
    public void onPause() {
        super.onPause();
        // 必须在生命周期结束时注销监听器,避免内存泄漏
        prefs.unregisterOnSharedPreferenceChangeListener(this);
    }

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        // 当指定键的值变化时更新TextView
        if ("DeviceToken".equals(key)) {
            String newSteps = sharedPreferences.getString(key, "0");
            tv_steps.setText(newSteps);
        }
    }

    public void startService(View view) {
        Intent serviceIntent = new Intent(getActivity(), StepsService.class);
        getActivity().startService(serviceIntent);
    }
}

方案2:用LocalBroadcastManager发送广播

Service每次更新计步数据后,发送一条本地广播,Fragment注册接收器接收广播并更新UI。

修改StepsService.java的run方法:

Runnable runnable = new Runnable() {
    public void run() {
        // 用Handler.postDelayed代替while(true)+sleep,更安全可控
        Handler handler = new Handler(Looper.getMainLooper());
        handler.post(new Runnable() {
            @Override
            public void run() {
                // 更新SharedPreferences
                SharedPreferences.Editor editor = getSharedPreferences("DeviceToken", MODE_PRIVATE).edit();
                String stepsStr = String.valueOf(stepsCounter);
                editor.putString("DeviceToken", stepsStr);
                editor.apply();
                addNotification(stepsStr);

                // 发送本地广播通知Fragment更新
                Intent intent = new Intent("com.yourpackage.STEPS_UPDATED");
                intent.putExtra("steps", stepsStr);
                LocalBroadcastManager.getInstance(StepsService.this).sendBroadcast(intent);

                // 延迟1秒再次执行
                handler.postDelayed(this, 1000);
            }
        });
    }
};

修改StepsFragment.java:

public class StepsFragment extends Fragment {
    private TextView tv_steps;
    private BroadcastReceiver stepsReceiver;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.your_fragment_layout, container, false);
        tv_steps = view.findViewById(R.id.your_textview_id);
        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        // 注册广播接收器
        stepsReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                String newSteps = intent.getStringExtra("steps");
                tv_steps.setText(newSteps);
            }
        };
        IntentFilter filter = new IntentFilter("com.yourpackage.STEPS_UPDATED");
        LocalBroadcastManager.getInstance(getActivity()).registerReceiver(stepsReceiver, filter);
    }

    @Override
    public void onPause() {
        super.onPause();
        // 注销接收器,避免内存泄漏
        LocalBroadcastManager.getInstance(getActivity()).unregisterReceiver(stepsReceiver);
    }

    public void startService(View view) {
        Intent serviceIntent = new Intent(getActivity(), StepsService.class);
        getActivity().startService(serviceIntent);
        // 首次加载当前存储的计步值
        SharedPreferences prefs = getActivity().getSharedPreferences("DeviceToken", MODE_PRIVATE);
        String currentSteps = prefs.getString("DeviceToken", "0");
        tv_steps.setText(currentSteps);
    }
}

方案3:使用LiveData(推荐,符合Android架构组件)

如果你的项目使用Jetpack组件,用LiveData管理计步数据是最优解,它能自动感知Fragment的生命周期,避免内存泄漏,代码也更简洁。

第一步:创建单例LiveData类

public class StepsLiveData extends MutableLiveData<String> {
    private static StepsLiveData instance;

    public static StepsLiveData getInstance() {
        if (instance == null) {
            instance = new StepsLiveData();
        }
        return instance;
    }
}

修改StepsService.java:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            String stepsStr = String.valueOf(stepsCounter);
            // 更新LiveData,自动通知观察者
            StepsLiveData.getInstance().setValue(stepsStr);
            // 同时更新SharedPreferences做持久化
            SharedPreferences.Editor editor = getSharedPreferences("DeviceToken", MODE_PRIVATE).edit();
            editor.putString("DeviceToken", stepsStr);
            editor.apply();
            addNotification(stepsStr);

            handler.postDelayed(this, 1000);
        }
    });
    return START_NOT_STICKY;
}

修改StepsFragment.java:

public class StepsFragment extends Fragment {
    private TextView tv_steps;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.your_fragment_layout, container, false);
        tv_steps = view.findViewById(R.id.your_textview_id);
        return view;
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 观察LiveData,自动更新UI
        StepsLiveData.getInstance().observe(getViewLifecycleOwner(), steps -> {
            tv_steps.setText(steps);
        });
    }

    public void startService(View view) {
        Intent serviceIntent = new Intent(getActivity(), StepsService.class);
        getActivity().startService(serviceIntent);
        // 首次加载SharedPreferences里的缓存值
        SharedPreferences prefs = getActivity().getSharedPreferences("DeviceToken", MODE_PRIVATE);
        String currentSteps = prefs.getString("DeviceToken", "0");
        tv_steps.setText(currentSteps);
    }
}
额外注意事项
  • 替换代码里的com.yourpackage为你自己的应用包名,广播Action要保证唯一。
  • 方案1和2中,一定要在Fragment的onPause或onDestroy里注销监听器/广播接收器,否则会造成内存泄漏。
  • Service里的while(true)+Thread.sleep是不安全的,建议用Handler.postDelayed或者Coroutine实现定时任务,这样在服务停止时可以移除回调,避免线程一直运行。

内容的提问来源于stack exchange,提问作者Jim Ng

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 06:32:47