每日一问:谈谈 SharedPreferences 的 apply() 和 commit() (3)

上面讲了初次获取一个文件的 SharedPreferences 实例的时候,会先去把所有键值对读取到缓存中,这明显是一个耗时操作,而我们正常的去读取数据的时候,都是类似这样的代码。

val sharedPreferences = getSharedPreferences("123", Context.MODE_PRIVATE) val string = sharedPreferences.getString("123","")

SharedPreferences 的 getXXX() 方法可能会报 ClassCastException 异常,所以我们在同一个 name 的时候,对不一样的类型,必须使用不同的 key。但是 putXXX 是可以用不同的类型值覆盖相同的 key 的。

那势必可能会导致这个操作需要等待一定的时间,我们姑且可以这么猜想,在 getXXX() 方法执行的时候应该是会等待前面的操作完成才能执行的。

因为 SharedPreferences 是一个接口,所以我们主要来看看它的实现类 SharedPreferencesImpl,这里以 getString() 为例。

@Override @Nullable public String getString(String key, @Nullable String defValue) { synchronized (mLock) { awaitLoadedLocked(); String v = (String)mMap.get(key); return v != null ? v : defValue; } }

awaitLoadedLocked() 方法应该就是我们所想的等待执行操作了,我们看看里面做了什么。

private void awaitLoadedLocked() { if (!mLoaded) { // Raise an explicit StrictMode onReadFromDisk for this // thread, since the real read will be in a different // thread and otherwise ignored by StrictMode. BlockGuard.getThreadPolicy().onReadFromDisk(); } while (!mLoaded) { try { mLock.wait(); } catch (InterruptedException unused) { } } if (mThrowable != null) { throw new IllegalStateException(mThrowable); } }

可以看到,在 awaitLoadedLocked 方法里面我们使用了 mLock.wait() 来等待初始化的读取操作,而我们前面看到的 loadFromDiskLocked() 方法的最后也可以看到它调用了 mLock.notifyAll() 方法来唤醒后面这个阻塞的 getXXX()。那么这里就会明显出现一个问题,我们的 getXXX() 方法是写在 UI 线程的,如果这个方法被阻塞的太久,势必会出现 ANR 的情况。所以我们一定在平时需要根据具体情况考虑是否需要把 SharedPreferences 的读写操作放在子线程中。

SharedPreferences 的内部类 Editor

我们在写入数据之前,总是要先通过类似这样的代码获取 SharedPreferences 的内部类 Editor。

val editor = sharedPreferences.edit()

我们当然要看看这个到底是什么东西。

@Override public Editor edit() { // TODO: remove the need to call awaitLoadedLocked() when // requesting an editor. will require some work on the // Editor, but then we should be able to do: // // context.getSharedPreferences(..).edit().putString(..).apply() // // ... all without blocking. synchronized (mLock) { awaitLoadedLocked(); } return new EditorImpl(); }

我们在

可以看到,我们在读取解析完 XML 文件的时候,直接返回了一个 Editor 的实现类 EditorImpl。我们随便查看一个 putXXX 的方法一看。

private final Object mEditorLock = new Object(); @GuardedBy("mEditorLock") private final Map<String, Object> mModified = new HashMap<>(); @GuardedBy("mEditorLock") private boolean mClear = false; @Override public Editor putString(String key, @Nullable String value) { synchronized (mEditorLock) { mModified.put(key, value); return this; } }

可以看到,我们在 EditorImpl 里面使用了一个 HashMap 来存放我们的键值对数据,每次 put 的时候都会直接往这个键值对变量 mModified 中进行数据的 put 操作。

commit() 和 apply()

我们总是在更新数据后需要加上 commit() 或者 apply() 来进行输入的写入操作,我们不妨来看看他们的实现到底有什么区别。

先看 commit() 和 apply() 的源码。

@Override public boolean commit() { long startTime = 0; if (DEBUG) { startTime = System.currentTimeMillis(); } MemoryCommitResult mcr = commitToMemory(); SharedPreferencesImpl.this.enqueueDiskWrite( mcr, null /* sync write on this thread okay */); try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException e) { return false; } finally { if (DEBUG) { Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration + " committed after " + (System.currentTimeMillis() - startTime) + " ms"); } } notifyListeners(mcr); return mcr.writeToDiskResult; } @Override public void apply() { final long startTime = System.currentTimeMillis(); final MemoryCommitResult mcr = commitToMemory(); final Runnable awaitCommit = new Runnable() { @Override public void run() { try { mcr.writtenToDiskLatch.await(); } catch (InterruptedException ignored) { } if (DEBUG && mcr.wasWritten) { Log.d(TAG, mFile.getName() + ":" + mcr.memoryStateGeneration + " applied after " + (System.currentTimeMillis() - startTime) + " ms"); } } }; QueuedWork.addFinisher(awaitCommit); Runnable postWriteRunnable = new Runnable() { @Override public void run() { awaitCommit.run(); QueuedWork.removeFinisher(awaitCommit); } }; SharedPreferencesImpl.this.enqueueDiskWrite(mcr, postWriteRunnable); // Okay to notify the listeners before it's hit disk // because the listeners should always get the same // SharedPreferences instance back, which has the // changes reflected in memory. notifyListeners(mcr); }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpyfpx.html