【问题标题】:Use SharedPreferences on multi-process mode在多进程模式下使用 SharedPreferences
【发布时间】:2015-03-05 19:59:26
【问题描述】:

我已经定义了一个在多进程模式下使用的SharedPreferences 实例。

public class Prefs {

    private static SharedPreferences prefs;
    private static SharedPreferences.Editor editor;

    private static void init(Context context) {

        prefs = context.getSharedPreferences("alaki",
                Context.MODE_MULTI_PROCESS);
        editor = prefs.edit();
    }

// static methods to set and get preferences
}

现在我在一个具有单独进程的服务上使用这个类,也在我的主应用程序进程中以静态方式使用这个类。
一切进展顺利,但有时已删除 SharedPreferences 实例上的所有存储数据!
我该如何解决这个问题?

编辑: 最后我通过 IPC 解决了我的问题。

【问题讨论】:

  • 修改后是否调用 editor.commit?
  • 嗯。当您说所有数据都已删除时,您是什么意思?一个进程是否在修改首选项,而另一个进程没有看到更改?
  • @jmm 不,两个进程都可以查看和修改,但我的数据不安全!例如,半小时后删除所有数据!

标签: android service multiprocessing sharedpreferences ipc


【解决方案1】:

目前无法在多个进程上安全访问SharedPreferences,如其documentation 中所述。

注意:该类不支持跨多个进程使用。

在使用MODE_MULTI_PROCESS 进行大量测试后,我要分享三个试验:

1- 在每个进程中初始化SharedPreferences一次并多次使用。

问题:这些值没有按预期反映在每个进程中。所以每个进程都有自己的 SharedPreferences 值。

2- 在每个 put 或 get 中初始化 SharedPreferences

这确实有效,并且值现在可以在进程之间互换。

问题:有时在积极访问 sharedpref 后,共享首选项文件及其所有内容被删除,如 issue 中所述,我在日志中收到以下警告:

W/FileUtils﹕ Failed to chmod(/data/data/com.hegazy.multiprocesssharedpref/shared_prefs/myprefs.xml): android.system.ErrnoException: chmod failed: ENOENT (No such file or directory)

您可以在问题中找到发生这种情况的原因。

3- 使用同步来锁定在SharedPreferences 中放置和获取值的方法。

这是完全错误的;同步不适用于跨进程。 SharedPreferences 实际上在其实现中使用了同步,但这只能确保线程安全,不能确保进程安全。这个描述得很好here

【讨论】:

  • 感谢您精心研究的答案!我发现它读起来有点混乱,所以我对其进行了编辑以整理英语,以便于理解。希望我保留了你原来的意思。
  • @AhmedHegazy 您的回答很有风险,我认为您应该考虑将值或对象发送到服务,或者每次存储值或对象更改偏好时,您应该考虑将值或对象发送给服务更新的值。这就是我所做的,我只是从两个过程之一中读取或存储首选项中的值。
  • @AlexSanchez 对不起,答案是关于什么的。它说您无法从多个进程访问共享首选项。
  • “这将稍后添加”这句话现已被删除。
  • @mhsmith 哦!不过我并没有等待:D
【解决方案2】:

SharedPreferences 本身不是进程安全的。这可能就是SharedPreferences documentation 说的原因

注意:目前此类不支持跨多个进程使用。这将在稍后添加。

【讨论】:

    【解决方案3】:

    我已经通过组合解决了这个问题:

    • 为每个进程提供对SharedPreferences 文件的互斥访问(例如通过使用a socket-based locking mechanism
    • 每次您想使用 SharedPreferences 绕过内存缓存时,都使用 MODE_MULTI_PROCESS 标志重新初始化它

    这似乎工作正常,但它还没有在现实世界中经过彻底的测试,所以我不知道它是否完全可靠。

    你可以看到我写的一个工作示例here

    警告:在 Android M 中,MODE_MULTI_PROCESS 似乎已被弃用。它将来可能会停止工作。

    【讨论】:

    • 你用创造性的方法解决问题的努力令人钦佩
    【解决方案4】:

    使用commit() 方法将更改存储在持久存储中,因此速度很慢,并且会在来自其他进程的多个调用之间产生冲突。

    不过这个方法还有一个替代方案,你应该调用apply()方法,这个方法把变化存储在内存中,然后异步存储在磁盘中,所以比较可靠。

    【讨论】:

    • 为什么您认为异步写入首选项文件会减少与其他进程的冲突?我刚刚仔细检查了SharedPreferencesImpl 源代码,它们都使用相同的代码(enqueueDiskWrite)来编写首选项文件。
    【解决方案5】:

    回想一下,使用上下文对象作为静态字段,你有上下文泄漏的风险,因为没有在应用程序类中声明对象

    public class CustomApplication extends Application{
         private Prefs prefs;
    
         public void onCreate(){
              prefs = new Prefs(this);
         }
    
         public Prefs getPrefs(){
            return prefs;
         }
    }
    

    您可以从任何上下文中获取首选项

       ((MyApplication)context.getApplicationContext()).getPrefs();
    

    【讨论】:

      【解决方案6】:

      使用使用 SharedPreferences 的内容提供程序。示例见这里:https://github.com/hamsterksu/MultiprocessPreferences

      【讨论】:

      • 我在生产中使用这种技术已经有一段时间了。它通常可以正常工作,但偶尔也会出现各种问题,这些问题似乎是 Android 中与内容提供程序相关的错误。
      【解决方案7】:

      如果两个进程向 SharedPreferences 写入数据,则可能所有 SharedPreferences 都被重置为默认值。

      您也可以在存储 val 之前尝试在编辑器上调用 clear()

      SharedPreferences.Editor sp = settings.edit();
      sp.clear();
      sp.putString("Name", "YourName");
      sp.commit();
      

      【讨论】:

      • clear() 将所有数据恢复为默认值。但是@Mousa 不想突然清除数据。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-03-16
      • 1970-01-01
      • 2010-10-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多