为何每次修改SharedPreferences都会触发FileObserver多类事件?
我实现了一个继承自FileObserver的自定义类,监听应用内部存储的shared_prefs目录。但每次执行以下代码时:
this.getSharedPreferences("fs_myfilename_", MODE_PRIVATE).edit().putString("Default", "value" + System.currentTimeMillis()).apply();
都会触发FileObserver.CREATE、FileObserver.MODIFY和FileObserver.DELETE事件。
我原本预期:首次写入触发CREATE事件,后续操作仅触发MODIFY事件,想知道为什么实际会同时触发多个事件。
更新内容:代码与日志
自定义FileObserver代码
public SharedPrefObserver(String path) { // 继承自FileObserver的类 super(path); } // 初始化与启动监听 mSharedPrefObserver = new SharedPrefObserver(getContext().getDataDir() + "/shared_prefs"); mSharedPrefObserver.startWatching();
onEvent重写方法
@Override public void onEvent(int event, @Nullable String path) { if (event == FileObserver.DELETE || event == FileObserver.CREATE || event == FileObserver.MODIFY) { Log.e(TAG, "Types of event: \n" + "1. Create: " + FileObserver.CREATE + "\n" + "2. Modify: " + FileObserver.MODIFY + "\n" + "3. Delete: " + FileObserver.DELETE + "\n" ); Log.e(TAG, "onEvent called for the event: " + event + "\n" + "path: " + path); } }
事件日志(仅监听CREATE、MODIFY、DELETE)
2023-10-13 05:50:27.161 2867-2953 SharedPrefObserver com.example.app E Types of event: 1. Create: 256 2. Modify: 2 3. Delete: 512 2023-10-13 05:50:27.160 2867-2953 SharedPrefObserver com.example.app E onEvent called for the event: 256 path: fs_myfilename_.xml 2023-10-13 05:50:27.161 2867-2953 SharedPrefObserver com.example.app E Types of event: 1. Create: 256 2. Modify: 2 3. Delete: 512 2023-10-13 05:50:27.161 2867-2953 SharedPrefObserver com.example.app E onEvent called for the event: 2 path: fs_myfilename_.xml 2023-10-13 05:50:27.172 2867-2953 SharedPrefObserver com.example.app E Types of event: 1. Create: 256 2. Modify: 2 3. Delete: 512 2023-10-13 05:50:27.172 2867-2953 SharedPrefObserver com.example.app E onEvent called for the event: 512 path: fs_myfilename_.xml.bak
解答
这是因为Android系统中SharedPreferences的apply()方法采用了临时文件写入+原子替换的实现逻辑,具体流程如下:
- 创建临时文件写入新内容:每次调用
apply()时,系统不会直接修改原fs_myfilename_.xml文件,而是先创建临时文件写入新配置——这会触发CREATE和MODIFY事件(日志中显示的fs_myfilename_.xml事件实际对应临时文件的操作)。 - 备份原文件:写入完成后,系统将原文件重命名为
fs_myfilename_.xml.bak作为备份。 - 替换原文件:把临时文件重命名为原文件名,完成配置更新。
- 删除备份文件:最后系统删除
.bak备份文件,触发DELETE事件。
这种设计是为了保证SharedPreferences的原子性:即使写入过程中发生崩溃,原文件或备份文件至少有一个是完整的,避免配置文件损坏。
如果只想关注用户实际修改配置的动作,可以通过过滤文件后缀(忽略.tmp和.bak文件),或者结合事件的时间顺序来判断一次完整的apply()操作完成时机。
内容的提问来源于stack exchange,提问作者Ghostop
相关产品推荐
相关产品推荐

