SharedPreferences的commit和apply

来源:互联网 发布:com.cn域名注册 编辑:程序博客网 时间:2024/05/29 15:04

SharedPreferences在安卓中是最常用的保存数据 的方式。

下面就了解一下SharedPreferences的commit和apply这两个提交数据的方法。

其实SharedPreferences和我们常用的Context都是接口,所以具体的实现方法其实是在SharedPreferencesImpl和ContextImpl中的

下面看一下源码:

public boolean commit() {    MemoryCommitResult mcr = commitToMemory();//获取需要保存的数据集合    SharedPreferencesImpl.this.enqueueDiskWrite(        mcr, null /* sync write on this thread okay */);//提交数据,并进行保存操作    try {        mcr.writtenToDiskLatch.await();//将当前线程挂起,等待写入线程操作执行完成    } catch (InterruptedException e) {        return false;    }    notifyListeners(mcr);//通知数据    return mcr.writeToDiskResult;}
可以看到最主要的写入数据到本地的方法就是enqueueDisWrite,然后闭锁等待所有文件写入成功,最后在返回写入结果。再看apply方法

public void apply() {    final MemoryCommitResult mcr = commitToMemory();//提取需要保存的数据    final Runnable awaitCommit = new Runnable() {            public void run() {                try {                    mcr.writtenToDiskLatch.await();//挂起该子线程,等待写入数据线程执行                } catch (InterruptedException ignored) {                }            }        };    QueuedWork.add(awaitCommit);//将等待线程加到工作队列中    Runnable postWriteRunnable = new Runnable() {            public void run() {                awaitCommit.run();                QueuedWork.remove(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);}
可以看到执行enqueueDiskWrite方法中传入了一个子线程runnable,接下来就是最重要的enqueueDiskWrite方法:

private void enqueueDiskWrite(final MemoryCommitResult mcr,                              final Runnable postWriteRunnable) {    final Runnable writeToDiskRunnable = new Runnable() {            public void run() {                synchronized (mWritingToDiskLock) {                    writeToFile(mcr);//                }                synchronized (SharedPreferencesImpl.this) {                    mDiskWritesInFlight--;                }                if (postWriteRunnable != null) {                    postWriteRunnable.run();                }            }        };//开启子线程写入数据到本地    final boolean isFromSyncCommit = (postWriteRunnable == null);    // Typical #commit() path with fewer allocations, doing a write on    // the current thread.    if (isFromSyncCommit) {        boolean wasEmpty = false;        synchronized (SharedPreferencesImpl.this) {            wasEmpty = mDiskWritesInFlight == 1;        }        if (wasEmpty) {            writeToDiskRunnable.run();//commit时直接在当前线程执行写数据任务            return;        }    }    QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable);//apply时,开启单个线程池执行任务}

变量isFromSyncCommit的值取决于是否传入一个子线程,如果传入了,就直接在当前线程中(有可能是主线程)执行写数据,然后返回,如果没有传入,就会通过QueuedWork.singleThreadExecutor创建一个单线程的线程池来执行写数据的线程.所以commit方法有可能会在主线程中执行造成线程阻塞,但是会同步读写数据;而apply则是直接将写数据丢到一个子线程中去执行.

因为apply中首先会将等待写入数据的线程加入到QuereWork中,

public static void waitToFinish() {    Runnable toFinish;    while ((toFinish = sPendingWorkFinishers.poll()) != null) {        toFinish.run();    }}
当中的waitToFinish会轮寻线程,当然这个方法在activity的onPause中会调用,所以当并发apply的时候在这里轮循就可能导致anr异常。

其次,apply方法如果在应用进程退出的时候,因为开启了子线程执行,所以可能不会正确的保存数据。例如当apply之后调用

android.os.Process.killProcess(android.os.Process.myPid());

然后在进来的时候就会发现数据并没有被正确保存,但是commit确实可以的



原创粉丝点击