Merge "Frameworks: Move SharedPreferencesImpl to Future"
am: aa3e9d351e
Change-Id: I2a47400d1e584aef1a16e05840ab964ca5735110
This commit is contained in:
@@ -50,6 +50,11 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.FutureTask;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
|
||||||
final class SharedPreferencesImpl implements SharedPreferences {
|
final class SharedPreferencesImpl implements SharedPreferences {
|
||||||
private static final String TAG = "SharedPreferencesImpl";
|
private static final String TAG = "SharedPreferencesImpl";
|
||||||
@@ -69,15 +74,11 @@ final class SharedPreferencesImpl implements SharedPreferences {
|
|||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
private final Object mWritingToDiskLock = new Object();
|
private final Object mWritingToDiskLock = new Object();
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
private Future<Map<String, Object>> mMap;
|
||||||
private Map<String, Object> mMap;
|
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private int mDiskWritesInFlight = 0;
|
private int mDiskWritesInFlight = 0;
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
|
||||||
private boolean mLoaded = false;
|
|
||||||
|
|
||||||
@GuardedBy("mLock")
|
@GuardedBy("mLock")
|
||||||
private StructTimespec mStatTimestamp;
|
private StructTimespec mStatTimestamp;
|
||||||
|
|
||||||
@@ -105,27 +106,18 @@ final class SharedPreferencesImpl implements SharedPreferences {
|
|||||||
mFile = file;
|
mFile = file;
|
||||||
mBackupFile = makeBackupFile(file);
|
mBackupFile = makeBackupFile(file);
|
||||||
mMode = mode;
|
mMode = mode;
|
||||||
mLoaded = false;
|
|
||||||
mMap = null;
|
mMap = null;
|
||||||
startLoadFromDisk();
|
startLoadFromDisk();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startLoadFromDisk() {
|
private void startLoadFromDisk() {
|
||||||
synchronized (mLock) {
|
FutureTask<Map<String, Object>> futureTask = new FutureTask<>(() -> loadFromDisk());
|
||||||
mLoaded = false;
|
mMap = futureTask;
|
||||||
}
|
new Thread(futureTask, "SharedPreferencesImpl-load").start();
|
||||||
new Thread("SharedPreferencesImpl-load") {
|
|
||||||
public void run() {
|
|
||||||
loadFromDisk();
|
|
||||||
}
|
|
||||||
}.start();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadFromDisk() {
|
private Map<String, Object> loadFromDisk() {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (mLoaded) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (mBackupFile.exists()) {
|
if (mBackupFile.exists()) {
|
||||||
mFile.delete();
|
mFile.delete();
|
||||||
mBackupFile.renameTo(mFile);
|
mBackupFile.renameTo(mFile);
|
||||||
@@ -158,16 +150,14 @@ final class SharedPreferencesImpl implements SharedPreferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
mLoaded = true;
|
|
||||||
if (map != null) {
|
if (map != null) {
|
||||||
mMap = map;
|
|
||||||
mStatTimestamp = stat.st_mtim;
|
mStatTimestamp = stat.st_mtim;
|
||||||
mStatSize = stat.st_size;
|
mStatSize = stat.st_size;
|
||||||
} else {
|
} else {
|
||||||
mMap = new HashMap<>();
|
map = new HashMap<>();
|
||||||
}
|
}
|
||||||
mLock.notifyAll();
|
|
||||||
}
|
}
|
||||||
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
static File makeBackupFile(File prefsFile) {
|
static File makeBackupFile(File prefsFile) {
|
||||||
@@ -226,36 +216,37 @@ final class SharedPreferencesImpl implements SharedPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void awaitLoadedLocked() {
|
private @GuardedBy("mLock") Map<String, Object> getLoaded() {
|
||||||
if (!mLoaded) {
|
try {
|
||||||
|
return mMap.get();
|
||||||
|
} catch (InterruptedException | ExecutionException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private @GuardedBy("mLock") Map<String, Object> getLoadedWithBlockGuard() {
|
||||||
|
if (!mMap.isDone()) {
|
||||||
// Raise an explicit StrictMode onReadFromDisk for this
|
// Raise an explicit StrictMode onReadFromDisk for this
|
||||||
// thread, since the real read will be in a different
|
// thread, since the real read will be in a different
|
||||||
// thread and otherwise ignored by StrictMode.
|
// thread and otherwise ignored by StrictMode.
|
||||||
BlockGuard.getThreadPolicy().onReadFromDisk();
|
BlockGuard.getThreadPolicy().onReadFromDisk();
|
||||||
}
|
}
|
||||||
while (!mLoaded) {
|
return getLoaded();
|
||||||
try {
|
|
||||||
mLock.wait();
|
|
||||||
} catch (InterruptedException unused) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, ?> getAll() {
|
public Map<String, ?> getAll() {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
return new HashMap<String, Object>(map);
|
||||||
//noinspection unchecked
|
|
||||||
return new HashMap<String, Object>(mMap);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getString(String key, @Nullable String defValue) {
|
public String getString(String key, @Nullable String defValue) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
String v = (String) map.get(key);
|
||||||
String v = (String)mMap.get(key);
|
|
||||||
return v != null ? v : defValue;
|
return v != null ? v : defValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,66 +254,65 @@ final class SharedPreferencesImpl implements SharedPreferences {
|
|||||||
@Override
|
@Override
|
||||||
@Nullable
|
@Nullable
|
||||||
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
|
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
@SuppressWarnings("unchecked")
|
||||||
Set<String> v = (Set<String>) mMap.get(key);
|
Set<String> v = (Set<String>) map.get(key);
|
||||||
return v != null ? v : defValues;
|
return v != null ? v : defValues;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getInt(String key, int defValue) {
|
public int getInt(String key, int defValue) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
Integer v = (Integer) map.get(key);
|
||||||
Integer v = (Integer)mMap.get(key);
|
|
||||||
return v != null ? v : defValue;
|
return v != null ? v : defValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public long getLong(String key, long defValue) {
|
public long getLong(String key, long defValue) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
Long v = (Long) map.get(key);
|
||||||
Long v = (Long)mMap.get(key);
|
|
||||||
return v != null ? v : defValue;
|
return v != null ? v : defValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public float getFloat(String key, float defValue) {
|
public float getFloat(String key, float defValue) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
Float v = (Float) map.get(key);
|
||||||
Float v = (Float)mMap.get(key);
|
|
||||||
return v != null ? v : defValue;
|
return v != null ? v : defValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public boolean getBoolean(String key, boolean defValue) {
|
public boolean getBoolean(String key, boolean defValue) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
Boolean v = (Boolean) map.get(key);
|
||||||
Boolean v = (Boolean)mMap.get(key);
|
|
||||||
return v != null ? v : defValue;
|
return v != null ? v : defValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contains(String key) {
|
public boolean contains(String key) {
|
||||||
|
Map<String, Object> map = getLoadedWithBlockGuard();
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
awaitLoadedLocked();
|
return map.containsKey(key);
|
||||||
return mMap.containsKey(key);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Editor edit() {
|
public Editor edit() {
|
||||||
// TODO: remove the need to call awaitLoadedLocked() when
|
// TODO: remove the need to call getLoaded() when
|
||||||
// requesting an editor. will require some work on the
|
// requesting an editor. will require some work on the
|
||||||
// Editor, but then we should be able to do:
|
// Editor, but then we should be able to do:
|
||||||
//
|
//
|
||||||
// context.getSharedPreferences(..).edit().putString(..).apply()
|
// context.getSharedPreferences(..).edit().putString(..).apply()
|
||||||
//
|
//
|
||||||
// ... all without blocking.
|
// ... all without blocking.
|
||||||
synchronized (mLock) {
|
getLoadedWithBlockGuard();
|
||||||
awaitLoadedLocked();
|
|
||||||
}
|
|
||||||
|
|
||||||
return new EditorImpl();
|
return new EditorImpl();
|
||||||
}
|
}
|
||||||
@@ -476,13 +466,43 @@ final class SharedPreferencesImpl implements SharedPreferences {
|
|||||||
// a memory commit comes in when we're already
|
// a memory commit comes in when we're already
|
||||||
// writing to disk.
|
// writing to disk.
|
||||||
if (mDiskWritesInFlight > 0) {
|
if (mDiskWritesInFlight > 0) {
|
||||||
// We can't modify our mMap as a currently
|
// We can't modify our map as a currently
|
||||||
// in-flight write owns it. Clone it before
|
// in-flight write owns it. Clone it before
|
||||||
// modifying it.
|
// modifying it.
|
||||||
// noinspection unchecked
|
// noinspection unchecked
|
||||||
mMap = new HashMap<String, Object>(mMap);
|
mMap = new Future<Map<String, Object>>() {
|
||||||
|
private Map<String, Object> mCopiedMap =
|
||||||
|
new HashMap<String, Object>(getLoaded());
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean cancel(boolean mayInterruptIfRunning) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCancelled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDone() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> get()
|
||||||
|
throws InterruptedException, ExecutionException {
|
||||||
|
return mCopiedMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> get(long timeout, TimeUnit unit)
|
||||||
|
throws InterruptedException, ExecutionException, TimeoutException {
|
||||||
|
return mCopiedMap;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
mapToWriteToDisk = mMap;
|
mapToWriteToDisk = getLoaded();
|
||||||
mDiskWritesInFlight++;
|
mDiskWritesInFlight++;
|
||||||
|
|
||||||
boolean hasListeners = mListeners.size() > 0;
|
boolean hasListeners = mListeners.size() > 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user