web-dev-qa-db-ja.com

SharedPreferencesとスレッドセーフ

SharedPreferences docs を見ると:

「注:現在、このクラスは複数のプロセスでの使用をサポートしていません。これは後で追加されます。」

したがって、それ自体はスレッドセーフではないようです。ただし、commit()およびapply()に関してどのような保証が行われますか?

例えば:

synchronized(uniqueIdLock){
   uniqueId = sharedPreferences.getInt("UNIQUE_INCREMENTING_ID", 0);
   uniqueId++;
   sharedPreferences.edit().putInt("UNIQUE_INCREMENTING_ID", uniqueId).commit();
}

この場合、uniqueIdは常に一意であることが保証されますか?

そうでない場合、持続するアプリケーションの一意のIDを追跡するより良い方法はありますか?

47
cottonBallPaws

プロセスとスレッドは異なります。 AndroidのSharedPreferences実装はスレッドセーフですが、プロセスセーフではありません。通常、アプリはすべて同じプロセスで実行されますが、AndroidManifest.xmlで設定することは可能です。たとえば、サービスはアクティビティなどとは別のプロセスで実行されます。

スレッドの安全性を確認するには、AOSPのContextImpl.JavaのSharedPreferenceImplを参照してください。あなたがそこにあると予想するところはどこでも同期されていることに注意してください。

private static final class SharedPreferencesImpl implements SharedPreferences {
...
    public String getString(String key, String defValue) {
        synchronized (this) {
            String v = (String)mMap.get(key);
            return v != null ? v : defValue;
        }
   }
...
    public final class EditorImpl implements Editor {
        public Editor putString(String key, String value) {
            synchronized (this) {
                mModified.put(key, value);
                return this;
            }
        }
    ...
    }
}

ただし、一意のIDの場合は、getとputの間で変更したくないので、同期が必要なようです。

85
Kevin TeslaCoil

私は同じことを疑問に思っていました-そして、遭遇しました このスレッド それはスレッドセーフではないと言います:

Context.getSharedPreferences()とEditor.commit()の実装は、同じモニターで同期しません。


それ以来、チェックするAndroid 14のコードを見て、それは非常に複雑です。具体的にはSharedPreferencesImplは、ディスクの読み書き時に異なるロックを使用しているようです:

  • enqueueDiskWrite()mWritingToDiskLockをロックします
  • startLoadFromDisk()thisをロックし、SharedPreferencesImpl.thisをロックするスレッドを起動します

このコードが本当に安全だとは思いません。

6

SharedPreferencesがSamsungのハンドセットでは機能しないことに注意してください。 Android issue をご覧ください。

github にあるシンプルなデータベース設定ストレージを実装しました。

乾杯、

4

それでうまくいくと思います。

同期セクション内でスリープを使用してテストし、異なるスレッドから呼び出すことができます

3
Pedro Loureiro