Android 共享数据引用的多模式处理是';行不通

Android 共享数据引用的多模式处理是';行不通,android,multiprocessing,sharedpreferences,Android,Multiprocessing,Sharedpreferences,我有一个SyncAdapter独立于主应用程序进程运行 我在SharedReferences周围使用一个静态包装器类,它在进程加载时创建一个静态对象(应用程序的onCreate),如下所示: 包装器具有get和set方法,如下所示: public static String getSomeString() { return myPrefs.getString(SOME_KEY, null); } public static void setSomeString(String str)

我有一个
SyncAdapter
独立于主应用程序进程运行

我在
SharedReferences
周围使用一个静态包装器类,它在进程加载时创建一个静态对象(应用程序的
onCreate
),如下所示:

包装器具有get和set方法,如下所示:

public static String getSomeString() {
    return myPrefs.getString(SOME_KEY, null);
}

public static void setSomeString(String str) {
    myPrefs.edit().putString(SOME_KEY, str).commit();
}
SyncAdapter
和应用程序都使用这个包装类来编辑和获取prefs,这有时有效,但很多时候我看到
SyncAdapter
在访问prefs时变旧/丢失prefs,而主应用程序正确地看到最近的更改

根据文档,我认为
MODE\u MULTI\u流程
标志应该像我期望的那样工作,允许两个流程都看到最新的更改,但它不起作用

更新:

根据
x90
的建议,我尝试避免使用静态
SharedReferences
对象,而是在每个get/set方法上调用
GetSharedReferences
。 这导致了一个新问题,即在多进程同时访问时删除prefs文件(!!!)。 i、 e.我在日志中看到:

(process 1): getName => "Name"
(process 2): getName => null
(process 1): getName => null
从那时起,保存在
SharedReferences
对象上的所有pref都被删除

这可能是我在日志中看到的另一个警告的结果:

W/FileUtils(21552): Failed to chmod(/data/data/com.my_company/shared_prefs/prefs_filename.xml): libcore.io.ErrnoException: chmod failed: ENOENT (No such file or directory)
这不是一个确定的问题,我在崩溃发生后看到了上面的日志,但还不能在同一台设备上重新创建,直到现在,它似乎还没有在其他设备上发生

另一个更新:


我已经就此提交了一份bug报告,在编写了一个小测试方法来确认这确实是安卓系统的问题后,将它挂在

上,我刚刚遇到了同样的问题。我切换了我的应用程序,在一个单独的进程中运行该服务,并意识到SharedReferences都被破坏了

两件事:

1) 您使用的是
编辑器.apply()
还是
提交()
?我正在使用
.apply()
。在活动或服务对其进行更改后,我开始检查我的首选项文件,并意识到无论何时进行更改,它都会创建一个仅包含新更改值的新文件。即,当从服务写入/更改新值时,从活动写入的值将被擦除,反之亦然我到处切换到
.commit()
,情况不再如此“请注意,当两个编辑器同时修改首选项时,最后一个调用apply的编辑器将获胜

2)
SharedReferencesListener
即使切换到
.commit()
,也似乎无法跨进程工作。您必须使用Messenger处理程序或广播意图来通知更改。当您查看SharedReferences类的文档时,它甚至会说“注意:当前此类不支持跨多个进程使用。这将在以后添加。”


在这方面,我们很幸运,我们甚至有了
MODE\u MULTI\u进程
标志,可以跨不同进程从同一个
SharedReference
进行读写操作。

SharedReferences本身不是进程安全的。(这可能就是SharedReferences文档中说“当前此类不支持跨多个进程使用。这将在以后添加。”)

MODE\u MULTI\u PROCESS
仅与每个
上下文一起工作。getSharedReferences(字符串名称,int MODE)
调用:当您检索SharedReferences实例时,指定
MODE\u MULTI\u PROCESS
标志,android将重新加载首选项文件,使其与任何(最终)一起更新对其进行的并发修改。如果将该实例保留为类(静态或非静态)成员,则不会再次重新加载首选项文件

每次想写入或读取首选项时使用
Context.getSharedReferences(…)
也不是进程安全的,但我想这可能是目前最接近的方法


如果您实际上不需要从不同的进程中读取相同的首选项,那么一个解决方法就是为不同的进程使用不同的首选项文件。

有完全相同的问题,我的解决方案是为SharedReferences编写一个基于ContentProvider的替换。它可以100%多进程工作

我让它成为我们所有人的图书馆。结果如下:

共享引用的模式多进程现在已经贬值(android M-API级别23以后)。它不是进程安全的。

模式多进程在API级别23中被弃用。您可以使用ContentProvider解决此问题。dReference使用ContentProvider包装sharepreference。它比使用sqlite Implemented具有更好的性能。

由于当前不支持模式多进程,我没有找到任何方法来处理进程之间的共享首选项,只能解决它

我知道人们正在共享他们为解决此问题而编写的库,但我实际上使用了我在上找到的第三方库,它实现了SQLLite而不是共享首选项:

然而,对我来说重要的是,我发现在其他解决方案中没有解决的问题是维护已经内置在首选项片段中的自动UI生成-能够在XML中指定元素并调用addPreferencesFromResource(R.XML.preferences)比从头构建UI更好

因此,为了实现这一点,我对所需的每个首选项元素(在我的示例中,仅为Preference、SwitchPreference和EditTextPreference)进行了子类化,并重写了基类中的一些方法,以包括保存到从上述库中获取的DatabaseSharedReference实例

F
W/FileUtils(21552): Failed to chmod(/data/data/com.my_company/shared_prefs/prefs_filename.xml): libcore.io.ErrnoException: chmod failed: ENOENT (No such file or directory)
public class EditTextDBPreference extends EditTextPreference {
private DatabaseBasedSharedPreferences mDBPrefs;
private String mKey;
private String mText;

public EditTextDBPreference(Context context) {
    super(context);
    init(context);
}

public EditTextDBPreference(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
}

public EditTextDBPreference(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
}

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public EditTextDBPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
    super(context, attrs, defStyleAttr, defStyleRes);
    init(context);
}

private void init(Context context)
{
    mDBPrefs = new DatabaseBasedSharedPreferences(context);
    mKey = super.getKey();
}

public DatabaseBasedSharedPreferences getSharedDBPreferences()
{
    if (mDBPrefs == null) {
        return null;
    }
    return mDBPrefs;
}

@Override
protected boolean persistBoolean(boolean value) {
    if (mKey != null)
        mDBPrefs.putBoolean(mKey,value);
    return super.persistBoolean(value);
}

@Override
protected boolean persistFloat(float value) {
    if (mKey != null)
        mDBPrefs.putFloat(mKey, value);
    return super.persistFloat(value);
}

@Override
protected boolean persistInt(int value) {
    if (mKey != null)
        mDBPrefs.putInt(mKey, value);
    return super.persistInt(value);
}

@Override
protected boolean persistLong(long value) {
    if (mKey != null)
        mDBPrefs.putLong(mKey, value);
    return super.persistLong(value);
}

@Override
protected boolean persistString(String value) {
    if (mKey != null)
        mDBPrefs.putString(mKey, value);
    return super.persistString(value);
}

@Override
protected boolean getPersistedBoolean(boolean defaultReturnValue) {
    if (mKey == null)
        return false;
    return mDBPrefs.getBoolean(mKey, defaultReturnValue);
}

@Override
protected float getPersistedFloat(float defaultReturnValue) {
    if (mKey == null)
        return -1f;
    return mDBPrefs.getFloat(mKey, defaultReturnValue);
}

@Override
protected int getPersistedInt(int defaultReturnValue) {
    if (mKey == null)
        return -1;
    return mDBPrefs.getInt(mKey, defaultReturnValue);
}

@Override
protected long getPersistedLong(long defaultReturnValue) {
    if (mKey == null)
        return (long)-1.0;
    return mDBPrefs.getLong(mKey, defaultReturnValue);
}

@Override
protected String getPersistedString(String defaultReturnValue) {
    if (mKey == null)
        return null;
    return mDBPrefs.getString(mKey, defaultReturnValue);
}

@Override
public void setKey(String key) {
    super.setKey(key);
    mKey = key;
}

@Override
protected void onSetInitialValue(boolean restoreValue, Object defaultValue) {
    setText(restoreValue ? getPersistedString(mText) : (String) defaultValue);
}

@Override
public void setText(String text) {
    final boolean wasBlocking = shouldDisableDependents();
    boolean textChanged = false;
    if (mText != null && !mText.equals(text))
        textChanged = true;
    mText = text;

    persistString(text);
    if (textChanged) {
        // NOTE: This is a an external class in my app that I use to send a broadcast to other processes that preference settings have changed
        BASettingsActivity.SendSettingsUpdate(getContext());
    }
    final boolean isBlocking = shouldDisableDependents();
    if (isBlocking != wasBlocking) {
        notifyDependencyChange(isBlocking);
    }
}

@Override
public String getText() {
    return mText;
}
<com.sampleproject.EditTextDBPreference
        android:key="@string/pref_key_build_number"
        android:title="@string/build_number"
        android:enabled="false"
        android:selectable="false"
        android:persistent="false"
        android:shouldDisableView="false"/>