在多进程模式下使用SharedPreferences

24

我已经定义了一个在多进程模式下使用的 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? - jjm
嗯,当你说所有数据都被删除时,你是什么意思?是一个进程修改了首选项而另一个进程没有看到更改吗? - jjm
1
@jmm 不,这两个进程都可以看到和修改我的数据,但我的数据不安全!例如,半小时后所有数据都被删除了! - Mousa Jafari
4
看这里:https://dev59.com/-WEh5IYBdhLWcg3wtFJE - Daniel Kopitchinski
8个回答

39

目前没有安全地在多个进程中访问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实际上在其实现中使用了同步,但这只能确保线程安全,而不能确保进程安全。这在此处中描述得非常好。

谢谢您提供这个经过深入研究的答案!我发现它有点难以理解,所以我编辑了一下,使英语更加清晰易懂。希望我保留了您原来的意思。 - Sam
@AhmedHegazy,你的回答有一定风险,我认为你应该考虑将值或对象发送到服务中,或者每当存储的值或对象在首选项中更改时,您都应该向服务发送带有更新值的消息。这就是我所做的,我只是从两个进程中的一个读取或存储首选项中的值。 - AXSM
@AlexSanchez 抱歉,这就是答案所涉及的内容。它指出您无法从多个进程访问共享首选项。 - Ahmed Hegazy
句子“This will be added later”现已被删除。 - mhsmith
@mhsmith 哦!不过我并没有在等待哦 :D - Ahmed Hegazy
显示剩余3条评论

8

SharedPreferences本身不具备进程安全性。这可能是为什么SharedPreferences文档中提到的原因:

注意:目前此类不支持跨多个进程使用,稍后将增加此功能。


3
我通过以下方式解决了这个问题:
  • 为每个进程提供互斥访问SharedPreferences文件(例如使用基于套接字的锁定机制)。
  • 在每次想要使用SharedPreferences时,使用MODE_MULTI_PROCESS标志重新初始化以绕过内存缓存。
这似乎可以正常工作,但在实际情况下并未经过彻底测试,因此我不知道它是否完全可靠。
您可以在这里查看我编写的一个工作示例。
警告:看起来MODE_MULTI_PROCESS已在Android M中被弃用。它可能在将来停止工作。

2
你用创新的方法解决问题的努力令人钦佩。 - Mousa Jafari

2

我已经在生产中使用这种技术有一段时间了。它通常有效,但也会出现各种偶发问题,这些问题似乎与内容提供程序相关的 Android bug 有关。 - Sam
它能工作,但会导致一些ANR。 - Shubham AgaRwal

2
使用commit()方法将更改存储在持久性存储中,因此它很慢,并且会导致来自其他进程的多个调用之间的冲突。
然而,有一种替代方法,您应该调用apply()方法,此方法将更改异步地存储在内存中,然后再存储在磁盘存储器中,因此更加可靠。

4
为什么你认为异步写入偏好文件会减少与其他进程的冲突?我刚刚仔细检查了SharedPreferencesImpl的源代码,它们都使用相同的代码(enqueueDiskWrite)来写入偏好文件。 - Sam

1
回顾使用上下文对象作为静态字段时,由于未在应用程序类中声明对象,存在上下文泄漏的风险。
public class CustomApplication extends Application{
     private Prefs prefs;

     public void onCreate(){
          prefs = new Prefs(this);
     }

     public Prefs getPrefs(){
        return prefs;
     }
}

从任何上下文中,您都可以获取首选项。
   ((MyApplication)context.getApplicationContext()).getPrefs();

0
 public static int getValore(Context ctx, String contenitore,  String chiave, int valore){
    try {
        SharedPreferences sh = ctx.getApplicationContext()
                .getSharedPreferences(contenitore, Context.MODE_MULTI_PROCESS);
        //SharedPreferences.Editor editor = sh.edit();
        return sh.getInt(chiave, valore);
    }catch (Exception ex){
        return valore;
    }
}

-2
如果两个进程都将数据写入SharedPreferences,则可能会将所有SharedPreferences重置为默认值。
此外,在存储val之前,您可以尝试在编辑器上调用clear()。
SharedPreferences.Editor sp = settings.edit();
sp.clear();
sp.putString("Name", "YourName");
sp.commit();

clear() 将所有数据返回到默认值。但是 @Mousa 不想突然清除数据。 - Hamidreza Samadi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接